简体   繁体   English

浮动与C中的双重奇怪行为

[英]float vs. double strange behaviour in c

i have troubles implementing a simple training program in C. The program should calculate a random cosinus or sinus of an angle, print the question "calculate cosinus/sinus of the angle x" to the user, who should type in the right answer in form "factor sqrt(value)". 我在用C实现简单的培训程序时遇到了麻烦。该程序应该计算一个随机的余弦或某个角度的窦,向用户打印“计算余弦/该角度x的窦”的问题,该用户应在表格中输入正确的答案“因素sqrt(值)”。 ie for cos(0) the user should type 1, for sin(45) the user should type 0.5sqrt(2) . 也就是说,对于cos(0),用户应键入1;对于sin(45),用户应键入0.5sqrt(2) Most of the code is given in this task. 大多数代码是在此任务中给出的。 The program doesn't work properly - for cos(270) the right answer is meant to be -0.000. 该程序无法正常工作-对于cos(270),正确答案应为-0.000。 Why is this happening? 为什么会这样呢? Why doesn't this code screams "division by 0"? 为什么此代码不尖叫“被0除”? Furthermore according to the task description the variable right should be of type double and rueckgabe of type int . 此外,根据任务描述,变量right应该是double类型,而rueckgabe应当是int类型。 But when i use double instead of float, i just get very high values (like 21234 or -435343). 但是当我使用double而不是float时,我得到的值非常高(例如21234或-435343)。 If i would use int as a return value of get_user_input(), the program won't work, right? 如果我将int用作get_user_input()的返回值,则该程序将无法正常工作,对吗?

Here's the code: 这是代码:

#include <stdio.h>
#include <math.h>
#include <time.h> 
#include <stdlib.h>

#define PI          (acos(-1)) 
#define ACCURACY    1e-4    

float get_user_input(is_cos, angle){ 
    if (is_cos == 1) {
        printf("Berechnen Sie den Cosinus zu %i\n", angle);
    }
    else {
        printf("Berechnen Sie den Sinus zu %i\n", angle);   
    }
    float faktor, wurzel=1.;
    float rueckgabe;
    scanf("%fsqrt(%f)", &faktor, &wurzel);

    rueckgabe = faktor * sqrt(wurzel);

    return rueckgabe;

}

int main (){
    float right;
    int correct;
    int angles[] = { 0, 30, 45, 60, 90, 180, 270, 360 }; 
    srand ( time(NULL) );
    int is_cos = rand()%2; 
    int angle = angles[ rand()%(sizeof(angles)/sizeof(int)) ];

    if( is_cos == 1) {
        right = cos(angle/180.*PI); 
    }
    else {
        right = sin(angle/180.*PI); 
    }


    correct = fabs(get_user_input(is_cos, angle)/right - 1.) <= ACCURACY;

    printf("Ihre Antwort war %s!\n", correct ? "richtig" : "falsch");
    return 0;
}

In your program, both divisor and dividend can be close to / exactly 0 at the same time. 在您的程序中,除数和被除数可以同时接近/正好为0。

Your program does not give you a "division by zero"-error, because by default most floating point implementations silently give you infinity/NaN/0/-0, depending on the exact values you divide. 您的程序不会给您“除以零”错误,因为默认情况下,大多数浮点实现都会根据您除以的确切值而无声地为您提供infinity / NaN / 0 / -0。

The cosine to 270° is not exactly zero in floating-point arithmetic, because 270° cannot be expressed exactly in radians. 浮点算术中的270°余弦值不完全为零,因为270°不能以弧度精确表示。

The following table shows the cosine of 270° (in the middle row) and in the first and last rows the cosines of the adjacent 32-bit floating-point numbers: 下表显示了270°的余弦(在中间行),在第一行和最后一行显示了相邻的32位浮点数的余弦:

                         phi                        cos(phi)
                   4.7123885                -0.0000004649123
                    4.712389               0.000000011924881
                   4.7123895                0.00000048876205

And the same for 64-bit double-precision floating-point numbers: 对于64位双精度浮点数也是如此:

                         phi                        cos(phi)
           4.712388980384689         -1.0718754395722282e-15
            4.71238898038469         -1.8369701987210297e-16
           4.712388980384691           7.044813998280222e-16

With the current floating-point precision, there's no way that cos(phi) with phi in the vicinity of 1.5*pi can be exactly zero. 在当前的浮点精度,有没有办法, cos(phi)phi在1.5 * PI附近可以精确为零。

You could fix that by writing a cosdeg that takes an argument in degrees and returns exact values for the angles where the cosines and sines are -1, 0 or 1 and values calulated with radians otherwise. 你可以通过写一个修复cosdeg ,取入度的参数,并返回用于在余弦和正弦与弧度否则计算方法为-1,0或1和值角度的精确值。 (Which then will happily generate the desired division by zero.) (然后将很高兴地生成期望的除以零的数。)

Since the sine and cosine return values in the range [-1, 1], I'd suggest that you use the absolute, rather than the relative, error. 由于正弦和余弦返回的值在[-1,1]范围内,因此建议您使用绝对误差而不是相对误差。 By replacing 通过更换

correct = fabs(get_user_input(is_cos, angle)/right - 1.) <= ACCURACY;

with

correct = fabs(get_user_input(is_cos, angle) - right) <= ACCURACY;

everything should work as expected. 一切都会按预期进行。

Generally I tend to use relative errors for large values and absolute errors for small values. 通常,我倾向于将相对误差用于大数值,将绝对误差用于小数值。 You can combine both with 您可以将两者结合

fabs(a-b)/(1.0+min(fabs(a), fabs(b)))

which (assuming you have a reasonable definition of min ) tends to the relative error for large values and to the absolute error for small ones. (假设您对min定义了一个合理的值)对于较大的值倾向于相对误差,对于较小的值倾向于绝对误差。

sin()和cos()的参数以弧度表示,而不是度数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM