math и debug: Математические функции и диагностика

Используйте математические функции для проверки диапазона и вывода диагностики.

Wave Foundation

Целочисленная функция для проверки диапазона

std::math::int
min_i32(a: i32, b: i32) -> i32
max_i32(a: i32, b: i32) -> i32
abs_i32_checked(x: i32) -> MathResult<i32>
clamp_i32_checked(x: i32, lo: i32, hi: i32) -> MathResult<i32>
div_floor_i32_checked(a: i32, b: i32) -> MathResult<i32>
div_ceil_i32_checked(a: i32, b: i32) -> MathResult<i32>

MathResult<T> содержит value и error. Импортируйте константы ошибок из std::math::result и используйте value только после проверки error == MATH_ERROR_NONE. Модуль минимального знакового целого не представим в том же типе, поэтому функция вычисления модуля с проверкой сообщает об ошибке. clamp отклоняет lo > hi. Деление проверяет нулевой делитель и переполнение диапазона. floor и ceil округляют иначе, чем целочисленное деление, которое отбрасывает дробную часть в направлении нуля.

Классификация значений с плавающей запятой

is_nan_f64, is_infinite_f64 и is_finite_f64 в std::math::float различают специальные значения. Также предусмотрена функция f32. float_to_bits_f64(value: f64) -> u64 — это функция получения битов памяти, которая отличается от числового преобразования value as u64. NaN даже не равен самому себе, поэтому он не сверяется с value == nan.

диагноз

debug_assert(condition: bool, message: str) в std::debug::core завершается после диагностики ложного состояния. Ситуации, которые обычно могут привести к сбою, например ввод данных пользователем, обрабатываются с помощью значения результата, а assert используется при проверке внутренних условий программы, которые должны быть выполнены.

Сохраните приведенную ниже программу как main.wave и запустите ее.

import("std::math::int")::{
    min_i32, max_i32
};
import("std::debug::core")::{
    debug_assert
};

fun main() {
    var low: i32 = min_i32(9, 4);
    var high: i32 = max_i32(9, 4);
    debug_assert(low <= high, "invalid range");
    println("{} {}", low, high);
}

Результат выполнения:

4 9

Направление округления для отрицательного деления

Сравните, как разделить -7 на 3. / усекается в сторону 0 и становится -2. floor выбирает меньшее целое число -3, а ceil выбирает большее целое число -2.

import("std::math::int")::{
    div_floor_i32_checked,
    div_ceil_i32_checked,
    abs_i32_checked
};
import("std::math::result")::{
    MathResult,
    MATH_ERROR_NONE,
    MATH_ERROR_OVERFLOW
};

fun main() -> i32 {
    var floor: MathResult<i32> = div_floor_i32_checked(-7, 3);
    var ceil: MathResult<i32> = div_ceil_i32_checked(-7, 3);

    if (floor.error != MATH_ERROR_NONE || ceil.error != MATH_ERROR_NONE) {
        return 1;
    }

    println("truncate={} floor={} ceil={}", -7 / 3, floor.value, ceil.value);

    var absolute: MathResult<i32> = abs_i32_checked(-2147483648);

    if (absolute.error == MATH_ERROR_OVERFLOW) {
        println("absolute value is outside i32");
    }

    return 0;
}

Результат выполнения:

truncate=-2 floor=-3 ceil=-2
absolute value is outside i32

Если вы проверяете только значение результата, вы не сможете отличить замещающее значение, включенное в случае сбоя, от фактического результата расчета. Сначала следуйте порядку проверки error. floor полезен при помещении отрицательных координат в интервал определенного размера, а ceil полезен при округлении необходимого количества связок.

Какие ошибки мне следует обрабатывать?

ситуация ошибка Пример обработки
Разделить на ноль MATH_ERROR_DIVIDE_BY_ZERO Снова принимает знаменатель
Результат не может быть сохранен в типе MATH_ERROR_OVERFLOW Вычислить более широкий тип или отклонить ввод
Минимум больше максимума в clamp MATH_ERROR_INVALID_ARGUMENT Изменить диапазон настроек

assert не является инструментом исправления ошибок. Ошибки пользовательского ввода обрабатываются с помощью условных операторов и возвращаемых значений, а после завершения вычислений внутренние условия, которые должны быть выполнены, проверяются с помощью debug_assert.