Visual c++ 在MSVC中将扩展精度浮点(80位)转换为双精度浮点(64位)
在MSVC win32/win64中,从扩展精度浮点(80位值,在某些编译器中也称为Visual c++ 在MSVC中将扩展精度浮点(80位)转换为双精度浮点(64位),visual-c++,x86,floating-point,long-double,extended-precision,Visual C++,X86,Floating Point,Long Double,Extended Precision,在MSVC win32/win64中,从扩展精度浮点(80位值,在某些编译器中也称为long double)到double(64位)的转换,什么是最方便和“正确”的方法 MSVC目前(截至2010年)假设long double是double同义词 我可能可以在内联asm中编写fld/fstp汇编程序对,但在MSVC中,内联asm不适用于win64代码。是否需要将此汇编代码移动到单独的.asm文件中?真的没有好的解决方案吗?如果您的编译器/平台没有80位浮点值的本机支持,您必须自己解码该值 假设8
long double
)到double(64位)的转换,什么是最方便和“正确”的方法
MSVC目前(截至2010年)假设long double
是double
同义词
我可能可以在内联asm中编写
fld
/fstp
汇编程序对,但在MSVC中,内联asm不适用于win64代码。是否需要将此汇编代码移动到单独的.asm文件中?真的没有好的解决方案吗?如果您的编译器/平台没有80位浮点值的本机支持,您必须自己解码该值
假设80位浮点存储在位于特定偏移量的字节缓冲区中,您可以这样做:
float64 C_IOHandler::readFloat80(IColl<uint8> buffer, uint32 *ref_offset)
{
uint32 &offset = *ref_offset;
//80 bit floating point value according to the IEEE-754 specification and the Standard Apple Numeric Environment specification:
//1 bit sign, 15 bit exponent, 1 bit normalization indication, 63 bit mantissa
float64 sign;
if ((buffer[offset] & 0x80) == 0x00)
sign = 1;
else
sign = -1;
uint32 exponent = (((uint32)buffer[offset] & 0x7F) << 8) | (uint32)buffer[offset + 1];
uint64 mantissa = readUInt64BE(buffer, offset + 2);
//If the highest bit of the mantissa is set, then this is a normalized number.
float64 normalizeCorrection;
if ((mantissa & 0x8000000000000000) != 0x00)
normalizeCorrection = 1;
else
normalizeCorrection = 0;
mantissa &= 0x7FFFFFFFFFFFFFFF;
offset += 10;
//value = (-1) ^ s * (normalizeCorrection + m / 2 ^ 63) * 2 ^ (e - 16383)
return (sign * (normalizeCorrection + (float64)mantissa / ((uint64)1 << 63)) * g_Math->toPower(2, (int32)exponent - 16383));
}
float64 C_IOHandler::readFloat80(IColl缓冲区,uint32*ref_偏移量)
{
uint32&offset=*ref\u offset;
//符合IEEE-754规范和标准Apple数字环境规范的80位浮点值:
//1位符号,15位指数,1位标准化指示,63位尾数
浮动64号;
if((缓冲区[偏移量]&0x80)==0x00)
符号=1;
其他的
符号=-1;
uint32指数=((uint32)缓冲区[offset]&0x7F)刚刚在x86代码中实现了这一点
.686P
.XMM
_TEXT SEGMENT
EXTRN __fltused:DWORD
PUBLIC _cvt80to64
PUBLIC _cvt64to80
_cvt80to64 PROC
mov eax, dword ptr [esp+4]
fld TBYTE PTR [eax]
ret 0
_cvt80to64 ENDP
_cvt64to80 PROC
mov eax, DWORD PTR [esp+12]
fld QWORD PTR [esp+4]
fstp TBYTE PTR [eax]
ret 0
_cvt64to80 ENDP
ENDIF
_TEXT ENDS
END
玩给出的答案,最后得到了这个
#include <cmath>
#include <limits>
#include <cassert>
#ifndef _M_X64
__inline __declspec(naked) double _cvt80to64(void* ) {
__asm {
// PUBLIC _cvt80to64 PROC
mov eax, dword ptr [esp+4]
fld TBYTE PTR [eax]
ret 0
// _cvt80to64 ENDP
}
}
#endif
#pragma pack(push)
#pragma pack(2)
typedef unsigned char tDouble80[10];
#pragma pack(pop)
typedef struct {
unsigned __int64 mantissa:64;
unsigned int exponent:15;
unsigned int sign:1;
} tDouble80Struct;
inline double convertDouble80(const tDouble80& val)
{
assert(10 == sizeof(tDouble80));
const tDouble80Struct* valStruct = reinterpret_cast<const tDouble80Struct*>(&val);
const unsigned int mask_exponent = (1 << 15) - 1;
const unsigned __int64 mantissa_high_highestbit = unsigned __int64(1) << 63;
const unsigned __int64 mask_mantissa = (unsigned __int64(1) << 63) - 1;
if (mask_exponent == valStruct->exponent) {
if(0 == valStruct->mantissa) {
return (0 != valStruct->sign) ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity();
}
// highest mantissa bit set means quiet NaN
return (0 != (mantissa_high_highestbit & valStruct->mantissa)) ? std::numeric_limits<double>::quiet_NaN() : std::numeric_limits<double>::signaling_NaN();
}
// 80 bit floating point value according to the IEEE-754 specification and
// the Standard Apple Numeric Environment specification:
// 1 bit sign, 15 bit exponent, 1 bit normalization indication, 63 bit mantissa
const double sign(valStruct->sign ? -1 : 1);
//If the highest bit of the mantissa is set, then this is a normalized number.
unsigned __int64 mantissa = valStruct->mantissa;
double normalizeCorrection = (mantissa & mantissa_high_highestbit) != 0 ? 1 : 0;
mantissa &= mask_mantissa;
//value = (-1) ^ s * (normalizeCorrection + m / 2 ^ 63) * 2 ^ (e - 16383)
return (sign * (normalizeCorrection + double(mantissa) / mantissa_high_highestbit) * pow(2.0, int(valStruct->exponent) - 16383));
}
#包括
#包括
#包括
#ifndef_M_X64
__内联数据规格(裸)双精度cvt80to64(空*){
__asm{
//公共cvt80to64程序
mov eax,dword ptr[esp+4]
fld T字节PTR[eax]
ret 0
//_cvt80to64 ENDP
}
}
#恩迪夫
#pragma包(推送)
#布拉格语包(2)
typedef无符号字符double80[10];
#布拉格语包(流行语)
类型定义结构{
无符号尾数:64;
无符号整数指数:15;
无符号整数符号:1;
}t双重结构;
内联双转换器Double80(const-tDouble80&val)
{
断言(10==sizeof(tDouble80));
const tDouble80Struct*valStruct=重新解释强制转换(&val);
常量无符号整数掩码指数=(1号)?-std::numeric_limits::infinity():std::numeric_limits::infinity();
}
//最高尾数位的设置意味着安静
返回(0!=(尾数高尾数位&valStruct->尾数))?std::numeric_limits::quiet_NaN():std::numeric_limits::signaling_NaN();
}
//符合IEEE-754规范的80位浮点值,以及
//标准Apple数字环境规范:
//1位符号,15位指数,1位标准化指示,63位尾数
常量双符号(valStruct->符号?-1:1);
//如果设置尾数的最高位,则这是一个标准化的数字。
未签名的尾数=valStruct->尾数;
双标准化校正=(尾数和尾数高最高位)!=0?1:0;
尾数&=面具尾数;
//值=(-1)^s*(归一化校正+m/2^63)*2^(e-16383)
返回(符号*(归一化校正+双精度(尾数)/尾数高最高位)*pow(2.0,int(valStruct->index)-16383);
}
我刚刚写了这个。它使用位运算从IEEE扩展精度数构造IEEE双精度数。它采用小端格式的10字节扩展精度数:
typedef unsigned long long uint64;
double makeDoubleFromExtended(const unsigned char x[10])
{
int exponent = (((x[9] << 8) | x[8]) & 0x7FFF);
uint64 mantissa =
((uint64)x[7] << 56) | ((uint64)x[6] << 48) | ((uint64)x[5] << 40) | ((uint64)x[4] << 32) |
((uint64)x[3] << 24) | ((uint64)x[2] << 16) | ((uint64)x[1] << 8) | (uint64)x[0];
unsigned char d[8] = {0};
double result;
d[7] = x[9] & 0x80; /* Set sign. */
if ((exponent == 0x7FFF) || (exponent == 0))
{
/* Infinite, NaN or denormal */
if (exponent == 0x7FFF)
{
/* Infinite or NaN */
d[7] |= 0x7F;
d[6] = 0xF0;
}
else
{
/* Otherwise it's denormal. It cannot be represented as double. Translate as singed zero. */
memcpy(&result, d, 8);
return result;
}
}
else
{
/* Normal number. */
exponent = exponent - 0x3FFF + 0x03FF; /*< exponent for double precision. */
if (exponent <= -52) /*< Too small to represent. Translate as (signed) zero. */
{
memcpy(&result, d, 8);
return result;
}
else if (exponent < 0)
{
/* Denormal, exponent bits are already zero here. */
}
else if (exponent >= 0x7FF) /*< Too large to represent. Translate as infinite. */
{
d[7] |= 0x7F;
d[6] = 0xF0;
memset(d, 0x00, 6);
memcpy(&result, d, 8);
return result;
}
else
{
/* Representable number */
d[7] |= (exponent & 0x7F0) >> 4;
d[6] |= (exponent & 0xF) << 4;
}
}
/* Translate mantissa. */
mantissa >>= 11;
if (exponent < 0)
{
/* Denormal, further shifting is required here. */
mantissa >>= (-exponent + 1);
}
d[0] = mantissa & 0xFF;
d[1] = (mantissa >> 8) & 0xFF;
d[2] = (mantissa >> 16) & 0xFF;
d[3] = (mantissa >> 24) & 0xFF;
d[4] = (mantissa >> 32) & 0xFF;
d[5] = (mantissa >> 40) & 0xFF;
d[6] |= (mantissa >> 48) & 0x0F;
memcpy(&result, d, 8);
printf("Result: 0x%016llx", *(uint64*)(&result) );
return result;
}
typedef无符号长uint64;
double MakedDoubleFromExtended(常量无符号字符x[10])
{
int exponent=((x[9]我认为对案例的处理if(exponent)修复了它。确切的exponent==0
案例确实可以产生一个非规范的双精度。它仍然不太正确:有事情要做的案例是exponent
在-52和0之间(代码的那个点)的案例,必须做的是大致使隐式位显式,将要使用的有效位向右移动-exponent
,并将exponent
设置为零。OP最终使用了FSTP
,因此这不是很重要,但您需要在模拟器中为FSTP
执行此操作:)划掉隐式位显式部分:前导1在尾数中已经显式了。只是在寻找其他内容时看到了这一点,而且…出于好奇,为什么您typedef unsigned\uu int64 uint64;
,而不是从
中使用uint64\u t
?是为了删除一个不属于r的依赖项吗因为您只使用了一个名称,因为您的编译器不支持它,或者是因为其他原因,所以很有必要使用它吗?