乘时浮点计算加一

时间:2018-08-19 22:07:01

标签: c floating-point

我正在计算c程序中的浮点数。我的值是6.3和2.8

有指数 10000001
10000000

我将它们相加并减去127,得到值130。

但是在在线计算器上,结果是131..so,我想知道这笔额外的钱是从哪里来的?

1 个答案:

答案 0 :(得分:0)

01000000110010011001100110011010  6.3
01000000001100110011001100110011  2.8
01000001100011010001111010111000  6.3*2.8

0 10000001 10010011001100110011010  6.3
0 10000000 01100110011001100110011  2.8
0 10000011 00011010001111010111000  6.3*2.8

1尾数中的1以格式表示,并且不存储,但是数学要求。

1.10010011001100110011010 * 2^2   (6.3)
1.01100110011001100110011 * 2^1   (2.8)
1.00011010001111010111000 * 2^4   (6.3*2.8)

使用小数点众所周知,如果您想乘以1.234 * 98.76,我们将1234 * 9876相乘就好像没有小数点,然后在这种情况下将小数点放在答案中3 + 2上的5位数字。对于单精度浮点数,归一化数字的点右边有23位数字。

所以我们先乘以24bit * 24bits

1100 1001 1001 1001 1001 1010  0xC9999A
1011 0011 0011 0011 0011 0011  0xb33333

0xC9999A * 0xB33333 = 0x8D1EBA47AE

现在将我们的点设置为46位。 46 = 11.5个十六进制数字,因此我们的结果为12个数字,所以在第一个数字的中间。

10.00110100011110101110000111... * 2^3
1.000110100011110101110000111... * 2^4  (normalize)

将尾数修剪为23位

1.00011010001111010111000 * 2^4

我们得到的结果与生产的计算机相同。

这就是为什么指数化是127 + 4而不是127 + 3的原因。

加法的工作原理与小学一样,只是我们将较小的数字移到右侧,将有效位数转储到以太中,以便对这一点进行排队,因此我们可以进行加法(或减法)。基本上是24位加24位= 25位加法。然后根据需要进行归一化。如果您认为这两个数字都为1.something,则msbit总是相加2(或3)。 1.x + 1.y = 10.z或11.z,如果有一个进位。

除法我们理想地希望精度,因此46位除以23位,因此填充分子,并使用标准二进制整数除法除以分母。我们知道两个数字都是1.something,所以像乘法一样,我们也确切地知道归一化之前将点放置在何处。

编辑

我的即席程序有多个问题。这是非法使用工会,这是几分钟的破解,而不是长期的解决方案,结果表明它恰好在使用此编译器的计算机上工作。我还假设unsigned int是32位。不要在一台计算机上使用几分钟来编写您要使用的代码。绝对不是长期代码。

#include <stdio.h>
union
{
    float f;
    unsigned int x;
} myun;
int main ( void )
{
    unsigned int ra;
    unsigned int rb;
    unsigned long long la,lb,lc;
    myun.f=6.3F;
    for(rb=0x80000000;rb;rb>>=1)
    {
        if(rb&myun.x) printf("1"); else printf("0");
    }
    printf("\n");
    myun.f=2.8F;
    for(rb=0x80000000;rb;rb>>=1)
    {
        if(rb&myun.x) printf("1"); else printf("0");
    }
    printf("\n");
    myun.f=2.8F*6.3F;
    for(rb=0x80000000;rb;rb>>=1)
    {
        if(rb&myun.x) printf("1"); else printf("0");
    }
    printf("\n");
    la = 0xC9999A;
    lb = 0xB33333;
    lc = la*lb;
    printf("0x%llX\n",lc);
    return(0);
}

一些合法的东西,但仍然有假设。

float one ( void )
{
    return 6.3F;
}
float two ( void )
{
    return 2.8F;
}
float three ( void )
{
    return 6.3F*2.8F;
}

编译然后反汇编

00000000 <one>:
   0:   e59f0000    ldr r0, [pc]    ; 8 <one+0x8>
   4:   e12fff1e    bx  lr
   8:   40c9999a    smullmi r9, r9, sl, r9  ; <UNPREDICTABLE>

0000000c <two>:
   c:   e59f0000    ldr r0, [pc]    ; 14 <two+0x8>
  10:   e12fff1e    bx  lr
  14:   40333333    eorsmi  r3, r3, r3, lsr r3

00000018 <three>:
  18:   e59f0000    ldr r0, [pc]    ; 20 <three+0x8>
  1c:   e12fff1e    bx  lr
  20:   418d1eb8            ; <UNDEFINED> instruction: 0x418d1eb8

我们得到了计算机来生成40c9999a 40333333和418d1eb8,并简单地从这些十六进制数字扩展位以查看此答案的开始。

乘法没什么不对的(好吧,我假设unsigned long long大于32位,那不是非法的,只是一个错误的假设),如果您没有可以使用的计算器,则很容易让计算机为您执行此操作。