【问题标题】:c# modulus operator on floatsc# 浮点数上的模运算符
【发布时间】:2021-02-20 08:33:26
【问题描述】:

下面的代码对数字 0.01、0.02、0.03、...、0.99 运行模数 0.01

var testVals = Enumerable.Range(1, 99).Select(n => double.Parse("0." + n.ToString("D2")));
var moduloTest = testVals.Select(v => v % 0.01).ToList();

(注意:从字符串中解析双精度数是有意的,这样对浮点数进行的唯一数学运算就是模数)

我希望这里的 moduleTest 列表包含几个非常接近 0 的浮点值。但是列表中的许多值反而非常接近 0.1。

我知道浮点数学并不精确,并且会引入舍入误差,但这里的 0.1 甚至不接近 0。

【问题讨论】:

  • 这能回答你的问题吗? Is floating point math broken?
  • @phuclv 我认为该页面上根本没有提到模数,所以我不认为它是重复的。我自己想出了一个答案,我在下面添加了它。
  • 模运算符在这里完全不相关。问题是二进制浮点类型不能正确表示所有十进制值,因此无论您使用什么运算符,无论是 +、-、* 还是 /,结果都会“不正确”
  • 我认为模数是相关的。大多数内置数学运算符都有平滑的曲线,这意味着您的结果至少会四舍五入到预期值。模数是一个例外,它具有锯齿曲线(请参阅 chux 答案),因此您可以得到一个不符合您期望的答案。

标签: c# floating-point modulo modulus


【解决方案1】:

我在打字时意识到了答案。由于浮动的不精确性,确实会发生这种情况。只是取模运算与大多数数学运算不同,值的微小不精确会导致结果的很大差异。

例如,0.03 在浮点中最接近地表示为 0.2999999999999999999,如果您要对其进行模数 0.01,则会给您一个像 0.0999999999999 这样的数字

【讨论】:

    【解决方案2】:

    我知道浮点数学并不精确,但在许多情况下,它似乎甚至不接近。

    这里的模数并没有添加错误/不精确性,只是使先前不精确性的影响非常明显。


    通过模数的高质量实现,没有不精确性。见Is fmod() exact when y is an integer?。

    问题在于假设数学与十进制值(如 0.01)的行为总是接近那些转换为最接近的可表示浮点数的值。有限浮点值都是精确的。形成它们的值的操作是“错误”出现的地方 - 除了一些例外,如模数。

    以十六进制或足够的十进制精度(如 17 个显着小数位)打印值通常足以表明预期的 0.01 不是编码为 0.01 的浮点数,而是一个 near 的值 那个。也可以对计算结果执行此操作以获得更深入的了解。

    取模运算的本质是一条sawtooth 曲线,类似于绿线here。

    鉴于在 0.01 转换和/(不是模数)中产生的错误,OP 值的模数结果预期就在不连续性的任一侧:大约 0.0 或接近 0.01 .

    【讨论】:

    • 这个答案提到了“/”(除法运算符),这不在我的原始问题中(其他人更改了我问题中的代码,我将其还原)。不过,这个答案仍然是正确的,并且比我的更能解释原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 2012-03-19
    • 2011-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多