Floating point 截断浮点值是否会产生其他错误?(AS3)

Floating point 截断浮点值是否会产生其他错误?(AS3),floating-point,truncate,deterministic,Floating Point,Truncate,Deterministic,我试图构建一个确定性模拟,通过以下函数截断浮点数: (我在这里找到的:) 我的问题是:我将它除以10000,这一事实本身不是浮点错误的来源吗? 也就是说,每次分割发生时,都会产生一个新的浮动,并可能产生更多不确定的结果 编辑: 这个怎么样?仅使用2的幂 return Math.round(float* 1024) / 1024; 当你说确定性时,我假设你想要一个可再现的模拟,每次运行模拟时都会得到完全相同的结果 要做到这一点,您需要找到可能的变化源并消除它 唯一的方法是编译为特定体系结构的二进

我试图构建一个确定性模拟,通过以下函数截断浮点数: (我在这里找到的:)

我的问题是:我将它除以10000,这一事实本身不是浮点错误的来源吗? 也就是说,每次分割发生时,都会产生一个新的浮动,并可能产生更多不确定的结果

编辑: 这个怎么样?仅使用2的幂

return Math.round(float* 1024) / 1024;

当你说确定性时,我假设你想要一个可再现的模拟,每次运行模拟时都会得到完全相同的结果

要做到这一点,您需要找到可能的变化源并消除它

唯一的方法是编译为特定体系结构的二进制文件

浮点运算本身是完全指定的。所有现代处理器都遵循浮点标准(IEEE-754),不存在歧义

主要有两种变化:

  • 指令集的差异。这是最明显的差异。如果将应用程序编译为32位或64位,可能会得到略有不同的结果。32位应用程序倾向于使用使用80位中间值的老式x87指令。这会导致某些结果的舍入方式不同。即使在x86上,如果使用SSE指令,也会有差异,因为SSE指令可以同时处理多个操作数。某些编译器可能生成的代码取决于操作数在内存中的对齐方式

  • 指令顺序的差异。在数学上,
    (a+b)+c
    a+(b+c)
    是等价的(加法是关联的)。在浮点计算中,情况并非如此。如果
    a
    是一,
    b
    是负一,
    c
    是一个很小的数字,使得
    1+c
    四舍五入到
    1
    ,那么表达式分别计算为
    c
    0
    。编译器决定使用哪些指令。根据您的语言和平台,它可能是语言编译器或即时IL/字节码编译器。无论哪种方式,编译器都是一个黑盒子,它可能会在我们不知情的情况下改变编译代码的方式。最小的差异可能导致不同的最终结果

  • 舍入法在理论上看起来不错,但不起作用。无论您如何取整,总会有两组不同但等效的指令产生不同取整结果的情况

    核心原因是舍入是不可组合的,即舍入到
    a
    位,然后舍入到
    b(
    位并不等于从一开始舍入到
    b
    位。例如:1.49四舍五入到一位为1.5,四舍五入到零位为2。但四舍五入到零位数直接得到1

    因此,在基于x87的系统上,使用80位“扩展”精度作为中间值,从64位有效位开始。您可以直接将其四舍五入到所需的精度。如果有双精度中间值,则得到相同的中间结果,但四舍五入到53个有效位,然后四舍五入到所需的精度

    您唯一的选择是为特定的体系结构生成机器代码

    现在,如果您的目标只是最小化差异,而不是完全消除差异,那么答案很简单:除以或乘以二的幂(如1024)不会在应用程序使用的范围内引入任何额外的舍入误差,而乘以或除以数字(如1000)则会引入任何额外的舍入误差


    如果将累积误差视为随机游动,那么使用1000进行舍入比使用1024进行舍入需要更多的步骤。乘法和除法都可能引入额外的错误。因此,平均而言,总误差将更大,因此舍入操作出错的可能性更大。当你对每个操作进行取整时,这甚至是正确的。

    当你说确定性时,我假设你想要一个可再现的模拟,每次运行模拟时都会得到完全相同的结果

    要做到这一点,您需要找到可能的变化源并消除它

    唯一的方法是编译为特定体系结构的二进制文件

    浮点运算本身是完全指定的。所有现代处理器都遵循浮点标准(IEEE-754),不存在歧义

    主要有两种变化:

  • 指令集的差异。这是最明显的差异。如果将应用程序编译为32位或64位,可能会得到略有不同的结果。32位应用程序倾向于使用使用80位中间值的老式x87指令。这会导致某些结果的舍入方式不同。即使在x86上,如果使用SSE指令,也会有差异,因为SSE指令可以同时处理多个操作数。某些编译器可能生成的代码取决于操作数在内存中的对齐方式

  • 指令顺序的差异。在数学上,
    (a+b)+c
    a+(b+c)
    是等价的(加法是关联的)。在浮点计算中,情况并非如此。如果
    a
    是一,
    b
    是负一,
    c
    是一个很小的数字,使得
    1+c
    四舍五入到
    1
    ,那么表达式分别计算为
    c
    0
    。编译器决定使用哪些指令。根据您的语言和平台,它可能是语言编译器或即时IL/字节码编译器。无论哪种方式,编译器都是一个黑盒子,它可能会在我们不知情的情况下改变编译代码的方式。最小的差别可以忽略不计
    return Math.round(float* 1024) / 1024;
    
    Machine 0 produces 0x1p+0 (1).
    Machine 1 produces 0x1.004p+0 (1.0009765625).
    The results differ.
    
    #include <stdio.h>
    #include <math.h>
    
    
    // Round a value to the nearest multiple of the quantum.
    static double Quantize(double x)
    {
        static const double Quantum = 1024., InverseQuantum = 1/Quantum;
    
        return round(x * Quantum) * InverseQuantum;
    }
    
    
    int main(void)
    {
        /*  For this example, we are in the middle of some calculation, where we
            have some value a from earlier operations.  a0 and a1 represent the
            calculated values of a on two different platforms.  Observe that the
            difference is as small as possible, just a single ULP.
        */
        double a0 = 0x1.cbd9f42000000p0;
        double a1 = 0x1.cbd9f42000001p0;
    
        // Define a constant that the calculation uses.
        double b = 0x1.1d2b9fp-1;
    
        // Calculate the pre-quantization result on each machine.
        double x0 = a0 * b;
        double x1 = a1 * b;
    
        // Quantize the result on each machine.
        double y0 = Quantize(x0);
        double y1 = Quantize(x1);
    
        // Display the results.
        printf("Machine 0 produces %a (%.53g).\n", y0, y0);
        printf("Machine 1 produces %a (%.53g).\n", y1, y1);
        printf("The results %s.\n", y0 == y1 ? "are identical" : "differ");
    
        return 0;
    }
    
    Math.round(1024 * float) / 1024;