检查浮点函数中的平凡解安全吗?

问题描述 投票:0回答:1

我正在编写代码来实现袖珍计算器,我的应用程序的要求之一是将用户与浮点怪癖隔离开来,例如

sin(M_PI)
不返回0。

由于我是为 ARM 皮质 M0 编写此内容,因此内存有限,这意味着没有像二进制编码的十进制那样奇特的东西,我必须坚持使用双精度数。我使用 newlib 作为数学库,它具有我需要的所有功能。

为了保护用户免受浮点实现问题(例如上面指出的问题)的影响,我提出的一种解决方案是修改 sin() 并检查简单情况,例如输入是

M_PI
的倍数。

例如:

double better_sin(double z) {
    double pi_ratio = z / M_PI;
    if (floor(pi_ratio) == pi_ratio) return 0;
    else return sin(z);
}

完成工作。

我的问题是,这有什么陷阱吗?速度并不重要,因为涉及人和键盘,因此所需的响应时间在毫秒范围内。

我尝试了一个简单的基准测试,看看这是否会搞砸 M_PI 周围的事情,但它似乎至少具有与正常 sin() 相同的“精度”。

int main(void) {

    double a;
    double result;
    
    printf("For input M_PI\n");
    a = M_PI;
    printf("\n");
    result = sin(nextafter(a, 3));
    printf("libc's sin(M_PI-): %.10e \n", result);
    result = better_sin(nextafter(a, 3));
    printf("better_sin(M_PI-): %.10e \n", result);
    printf("\n");
    result = sin(a);
    printf("libc's sin(M_PI): %.10e \n", result);
    result = better_sin(a);
    printf("better_sin(M_PI): %.10e \n", result);
    printf("\n");
    result = sin(nextafter(a, 4));
    printf("libc's sin(M_PI+): %.10e \n", result);
    result = better_sin(nextafter(a, 4));
    printf("better_sin(M_PI+): %.10e \n", result);

    printf("\n\n");

    a = 50e6 * M_PI;
    printf("\n");
    result = sin(nextafter(a, 3));
    printf("libc's sin(50E6 * M_PI-): %.10e \n", result);
    result = better_sin(nextafter(a, 3));
    printf("better_sin(50E6 * M_PI-): %.10e \n", result);
    printf("\n");
    result = sin(a);
    printf("libc's sin(50E6 * M_PI): %.10e \n", result);
    result = better_sin(a);
    printf("better_sin(50E6 * M_PI): %.10e \n", result);
    printf("\n");
    result = sin(nextafter(a, 4));
    printf("libc's sin(50E6 * M_PI+): %.10e \n", result);
    result = better_sin(nextafter(a, 4));
}

返回:

libc's sin(M_PI-): 5.6655388976e-16 
better_sin(M_PI-): 5.6655388976e-16 

libc's sin(M_PI): 1.2246467991e-16 
better_sin(M_PI): 0.0000000000e+00 

libc's sin(M_PI+): -3.2162452994e-16 
better_sin(M_PI+): -3.2162452994e-16 


libc's sin(M_PI-): -4.9343786466e-08 
better_sin(M_PI-): -4.9343786466e-08 

libc's sin(M_PI): -1.9541464078e-08 
better_sin(M_PI): -1.9541464078e-08 

libc's sin(M_PI+): -4.9343786466e-08 
better_sin(M_PI+): -4.9343786466e-08 

这将需要编写大量代码,以确保科学计算器的所有功能都能为琐碎的情况返回正确的答案(如上所示),因此,如果我遗漏了更大的东西,任何输入都会受到赞赏。

c floating-point trigonometry newlib
1个回答
0
投票

这种努力基本上是徒劳的。没有一个数值系统能够完全实现实数运算,甚至不能实现任意精度运算或符号运算。

检查

M_PI
的倍数将会失败,因为 π 是无理数(且是超越数)。
M_PI
无法捕获 π 的完整值,因此它的倍数与 π 的倍数越来越远,而其他浮点值将比
M_PI
最接近的倍数更接近 π 的倍数。

此外,您还必须拼凑许多其他数值算术工件,例如

1/49*49-1
不产生零。

您需要重新考虑“用户与浮点怪癖隔离……”的要求,要么放弃它,要么完全准确地说明它是什么,充分考虑数值算术的属性可以实现什么。

© www.soinside.com 2019 - 2024. All rights reserved.