assembly 使用汇编将摄氏温度转换为华氏温度,然后再转换回来

mu0hgdu0  于 2023-05-18  发布在  其他
关注(0)|答案(1)|浏览(101)

我使用以下x86汇编指令将摄氏度转换为华氏度:

imul dword [NINE]
  idiv dword [FIVE]
  add  eax, 32

对于相当多的输入,这会产生一个off-by-one错误:
现在,我通过检查有符号的除法除以5的余数,然后相应地舍入来解决这个问题:

imul dword [NINE]
  idiv dword [FIVE]   ; -> Remainder EDX=[-4,4]
  add  eax, 32+1
  cmp  edx, 2
  jg   @f
  dec  eax
  cmp  edx, -2
  jge  @f
  dec  eax
@@:

我更喜欢无分支的解决方案,因为我听说它通常会更快。
另外,我怎样才能改变代码,使它返回一个小数?

guz6ccqo

guz6ccqo1#

从摄氏度到华氏度

在x86汇编中,将摄氏温度转换为华氏温度的直接实现使用以下公式:

Fahrenheit = (Celsius * 9 / 5) + 32
; IN (eax) OUT (eax) MOD (edx)
C2F_a:  imul    dword [NINE]
        idiv    dword [FIVE]
        add     eax, 32
        ret

需要有符号运算,因为摄氏温度和华氏温度也存在于负数范围内。它们的刻度从absolute zero(0 K)开始:Celsius的起始温度为-273.15 °C,Fahrenheit的起始温度为-459.67 °F。

取整

上述代码的一个重要缺点是结果不能正确地舍入到最近的整数。(idiv向零舍入,也称为截断。)实现这种舍入的最简单方法是在被除数除以5之前将 * 加 * 2。可悲的是,这只适用于积极的投入。对于负输入,我们宁愿在除以5之前减去 * 2。
因此,下一个解决方案通过首先向输入添加合适的偏置来避免 * 有符号 * 除法。代码的其余部分不再需要考虑负数。275的偏差将使任何法律的输入为正,并且不会引入进一步的舍入问题,因为最终将消除偏差的值是整数:(275 * 9 / 5)= 495。

Fahrenheit = (((Celsius + 275) * 9) + 2) / 5 + (32 - 495)
; IN (eax) OUT (eax) MOD (edx)
C2F_b:  add     eax, 275
        imul    eax, 9
        add     eax, 2
        xor     edx, edx
        div     dword [FIVE]
        add     eax, 32-495
        ret

优化

可以用一条LEA指令来代替9的乘法运算,该指令可以同时执行几次加法运算。
同样可以用乘以5的multiplicative inverse来代替除以5。

Fahrenheit = (((Celsius + 275) * 9) + 2) / 5 + (32 - 495)
; IN (eax) OUT (eax) MOD (edx)
C2F_c:  lea     eax, [eax+eax*8+(275*9)+2]
        mov     edx, 0CCCCCCCDh
        mul     edx
        shr     edx, 2
        lea     eax, [edx+32-495]
        ret

精度

使用十进制fixed-point numbers,缩放比例为1/100,我们可以提供精确到小数点后两位的结果。例如,-273.15 °C被存储为带符号整数-27315,并将输出表示-459.67 °F的带符号整数-45967。

Fahrenheit = (((Celsius + 27500) * 9) + 2) / 5 + (3200 - 49500)
; IN (eax) OUT (eax) MOD (edx)
C2F_d:  lea     eax, [eax+eax*8+(27500*9)+2]
        mov     edx, 0CCCCCCCDh
        mul     edx
        shr     edx, 2
        lea     eax, [edx+3200-49500]
        ret
摄氏度C2F_aC2F_bC2F_cC2F_d联系我们
-10十四岁十四岁十四岁十四点十四岁
-9十六岁十六岁十六岁十五块八十五点八
-8十八岁十八岁十八岁十七块六毛十七点六
-7二十!十九岁十九岁十九点四十十九点四
-6二十二!二十一二十一二十一点二十二十一点二
-5二十三二十三二十三二十三点二十三
-4个二十五二十五二十五二十四点八二十四点八
-3二十七二十七二十七二十六点六二十六点六
-2二十九!二十八二十八二十八点四十二十八点四
-1三十一!三十三十三十点二十30.2
0三十二三十二三十二三十二点三十二
1三十三!34人34人三十三点八三十三点八
三十五!三十六三十六三十五块六三十五点六
三十七三十七三十七三十七点四十三十七点四
三十九三十九三十九三十九点二十三十九点二
四十一四十一四十一四十一点四十一
四十二!四十三四十三四十二点八四十二点八
四十四!四十五四十五四十四点六四十四点六
四十六四十六四十六四十六点四十四十六点四
四十八四十八四十八四十八点二十四十八点二
10个五十五十五十50块五十

从华氏度到摄氏度

在x86汇编中,将华氏温度转换为摄氏温度的直接实现使用以下公式:

Celsius = (Fahrenheit - 32) * 5 / 9
; IN (eax) OUT (eax) MOD (edx)
F2C_a:  sub     eax, 32
        imul    dword [FIVE]
        idiv    dword [NINE]
        ret

需要有符号运算,因为摄氏温度和华氏温度也存在于负数范围内。它们的刻度从absolute zero(0 K)开始:Celsius的起始温度为-273.15 °C,Fahrenheit的起始温度为-459.67 °F。

取整

上述代码的一个重要缺点是结果不能正确地舍入到最近的整数。实现这种四舍五入的最简单方法是在除以9之前将被除数 * 加 * 4。可悲的是,这只适用于积极的投入。对于负输入,我们宁愿在除以9之前减去 * 4。
因此,下一个解决方案通过首先向输入添加合适的偏置来避免 * 有符号 * 除法。代码的其余部分不再需要考虑负数。偏置495将使任何法律的输入为正,并且不会引入进一步的舍入问题,因为最终将消除偏置的值是整数:(495 * 5 / 9)= 275。

Celsius = (((Fahrenheit + 495 - 32) * 5) + 4) / 9 - 275
; IN (eax) OUT (eax) MOD (edx)
F2C_b:  add     eax, 495-32
        imul    eax, 5
        add     eax, 4
        xor     edx, edx
        div     dword [NINE]
        sub     eax, 275
        ret

优化

可以用一条LEA指令来代替5的乘法运算,该指令同时可以进行几次加法运算。
同样可以用乘以9的multiplicative inverse来代替除以9。

Celsius = (((Fahrenheit + 495 - 32) * 5) + 4) / 9 - 275
; IN (eax) OUT (eax) MOD (edx)
F2C_c:  lea     eax, [eax+eax*4+(495-32)*5+4]
        mov     edx, 38E38E39h
        mul     edx
        shr     edx, 1
        lea     eax, [edx-275]
        ret

精度

使用十进制fixed-point numbers,缩放比例为1/100,我们可以提供精确到小数点后两位的结果。例如-459.67 °F被存储为带符号整数-45967,并将输出表示-273.15 °C的带符号整数-27315。

Celsius = (((Fahrenheit + 49500 - 3200) * 5) + 4) / 9 - 27500
; IN (eax) OUT (eax) MOD (edx)
F2C_d:  lea     eax, [eax+eax*4+(49500-3200)*5+4]
        mov     edx, 38E38E39h
        mul     edx
        shr     edx, 1
        lea     eax, [edx-27500]
        ret
°FF2C_aF2C_bF2C_cF2C_d联系我们
-10二十三二十三二十三-23.33-23.333333
-9-22!二十三二十三-22.78-22.777777
-8二十二二十二二十二-22.22-22.222222
-7-21!二十二二十二-21.67-21.666666
-6二十一二十一二十一-21.11-21.111111
-5-20!二十一二十一-20.56-20.555555
-4个-20-20-20-20.00-20
-3-19-19-19-19.44-19.444444
-2十八岁!-19-19-18.89-18.888888
-1十八岁十八岁十八岁-18.33-18.333333
0十七岁!十八岁十八岁-17.78-17.777777
1十七岁十七岁十七岁-17.22-17.222222
- 十六!十七岁十七岁-16.67-16.666666
-16-16-16-16.11-16.111111

| 四|-15!|-16|-16| -15.56 | -15.555555 |
| 五|-15|-15|-15| -15.00 |-15|
| 六|-14|-14|-14| -14.44 | -14.444444 |
| 七|-13!|-14|-14| -13.89 | -13.888888 |
| 八|-13|-13|-13| -13.33 | -13.333333 |
| 九|-12!|-13|-13| -12.78 | -12.777777 |
| 10个|-12|-12|-12| -12.22 | -12.222222 |

相关问题