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 导入错误常量,确认 error == MATH_ERROR_NONE 后才能使用 value。有符号整数最小值的绝对值无法用同一类型表示,因此带检查的绝对值函数会报告失败。clamp 拒绝 lo > hi。除法会检查除数为零及结果溢出的情况。floor 和 ceil 的取整方式不同于向零截断的整数除法。

对浮点值进行分类

std::math::float中的is_nan_f64、is_infinite_f64、is_finite_f64区分特殊值。还提供f32功能。 float_to_bits_f64(value: f64) -> u64是获取存储位的函数,与value as u64的数值转换不同。 NaN 甚至不等于其自身,因此不会针对 value == nan 进行检查。

诊断

std::debug::core 中的debug_assert(condition: bool, message: str) 在诊断出错误条件后终止。通常可能失败的情况,例如用户输入,通过结果值进行处理,并且在检查必须满足的内部程序条件时使用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检查必须满足的内部条件。