如果没有类型转换对应的汇编指令,我们可以用浮点加法来完成整型和浮点类型的互转。
注意:需要实现IEE浮点环境(float,double数据类型),不能用FPU的80位浮点做临时运算!
如果有对应的汇编指令,调用之明显更简单高效,然而有些情况下类型转换会溢出,这要注意,计算机不会检查溢出而返回错误结果。一般来说,一次整型和浮点类型的互转的消耗和浮点加法一样,实际就是用浮点加法实现的类型转换。代码如下(不解释):
typeconvert.h
#include <stdint.h>
//以下所有实现经过了严格测试,可以保证无误。其中,标准实现不会溢出,而用指令转换可能溢出。
/**
* 转换後,得到32位有符号整型(未考虑转换溢出)。
**/
int32_t convert_to_int32(double a)
{
a+=6755399441055744.0;
return *(int32_t*)&a;
}
/**
* 转换後,得到23位有效数字之有符号整型(未考虑转换溢出)。即,a in[-0x400000,0x400000.8]
**/
int32_t convert_to_int23(float a)
{
a+=12582912.0f;
return *(int32_t*)&a-0x4B400000;
}
/**
* 转换後,得到24位有效数字之有符号整型(未考虑转换溢出)。即,a in[-0x7FFFFF,0x7FFFFF]
**/
int32_t convert_to_int24(float a)
{
if(a<0)
{
a-=8388608.0f;
return -((*(int32_t*)&a)&0x7FFFFF);
}
else
{
a+=8388608.0f;
return (*(int32_t*)&a)&0x7FFFFF;
}
}
/**
* 标准实现,考虑了转换溢出的问题
**/
int32_t convert_to_int32(float a)
{
float convert23_to_float(int32_t i);
if(a>=-(float)(1<<31))return (1<<31)-1;
if(a<(float)(1<<31))return (1<<31);
int32_t n=(*(int32_t*)&a)>>31;
int32_t i;
(*(int32_t*)&a)&=0x7FFFFFFF;
if(a>8388607.0f)
{
(*(int32_t*)&a)-=0x7800000;
float b=a+8388608.0f;
i=(*(int32_t*)&b)&0x7FFFFF;
a-=convert23_to_float(i);
a+=384.0f;
i=(i<<15)+*(int16_t*)&a;
}
else
{
a+=8388608.0f;
i=(*(int32_t*)&a)&0x7FFFFF;
}
return (i^n)-n;
}
/**
* 转换後,得到52位有效数字之有符号整型(未考虑转换溢出)。即,a in[-0x8000000000000LL,0x8000000000000.8LL]
**/
int64_t convert_to_int52(double a)
{
a+=6755399441055744.0;
return *(int64_t*)&a-0x4338000000000000LL;
}
/**
* 转换後,得到53位有效数字之有符号整型(未考虑转换溢出)。即,a in[-0xFFFFFFFFFFFFFLL,0xFFFFFFFFFFFFFLL]
**/
int64_t convert_to_int53(double a)
{
if(a<0)
{
a-=4503599627370496.0;
return -((*(int64_t*)&a)&0xFFFFFFFFFFFFFLL);
}
else
{
a+=4503599627370496.0;
return (*(int64_t*)&a)&0xFFFFFFFFFFFFFLL;
}
}
/**
* 标准实现,考虑了转换溢出的问题
**/
int64_t convert_to_int64(double a)
{
double convert52_to_double(int64_t i);
if(a>=-(double)(1LL<<63))return (1LL<<63)-1;
if(a<(double)(1LL<<63))return (1LL<<63);
int64_t n=(*(int64_t*)&a)>>63;
int64_t i;
(*(int64_t*)&a)&=0x7FFFFFFFFFFFFFFFLL;
if(a>4503599627370495.0)
{
(*(int64_t*)&a)-=0x1F0000000000000LL;
double b=a+4503599627370496.0;
i=(*(int64_t*)&b)&0xFFFFFFFFFFFFFLL;
a-=convert52_to_double(i);
a+=3145728.0;
i=(i<<31)+*(int32_t*)&a;
}
else
{
a+=4503599627370496.0;
i=(*(int64_t*)&a)&0xFFFFFFFFFFFFFLL;
}
return (i^n)-n;
}
/**
* 标准实现,考虑了转换溢出的问题
**/
int64_t convert_to_int64(float a)
{
return convert_to_int64((double)a);
}
/**
* i是23位有效数字之有符号整型。即,i in[-0x400000,0x400000]
**/
float convert23_to_float(int32_t i)
{
i+=0x4B400000;
return (*(float*)&i)-12582912.0f;
}
/**
* i是24位有效数字之有符号整型。即,i in[-0x7FFFFF,0x7FFFFF]
**/
float convert24_to_float(int32_t i)
{
if(i<0)
{
i=0xCB000000-i;
return (*(float*)&i)+8388608.0f;
}
else
{
i+=0x4B000000;
return (*(float*)&i)-8388608.0f;
}
}
/**
* 标准实现
**/
float convert_to_float(int32_t i)
{
int32_t n=i>>31;
float a;
i=(i^n)-n;
if((uint32_t)i>8388607)
{
int32_t j=(uint32_t)i>>23;
i&=0x7FFFFF;
i+=0x4B000000;
a=(*(float*)&i)-8388608.0f;
j+=0x56800000;
a+=(*(float*)&j)-(float)(1LL<<46);
}
else
{
i+=0x4B000000;
a=(*(float*)&i)-8388608.0f;
}
(*(int32_t*)&a)|=(n&0x80000000);
return a;
}
/**
* 标准实现
**/
float convert_to_float(int64_t i)
{
double convert_to_double(int64_t i);
return (float)convert_to_double(i);
}
/**
* i是53位有效数字之有符号整型。即,i in[-0xFFFFFFFFFFFFFLL,0xFFFFFFFFFFFFFLL]
**/
double convert53_to_double(int64_t i)
{
if(i<0)
{
i=0xC330000000000000LL-i;
return (*(double*)&i)+4503599627370496.0f;
}
else
{
i+=0x4330000000000000LL;
return (*(double*)&i)-4503599627370496.0f;
}
}
/**
* i是52位有效数字之有符号整型。即,i in[-0x8000000000000LL,0x7FFFFFFFFFFFFLL]
**/
double convert52_to_double(int64_t i)
{
i+=0x4338000000000000LL;
return (*(double*)&i)-6755399441055744.0;
}
/**
* 标准实现
**/
double convert_to_double(int32_t i)
{
return convert52_to_double((int64_t)i);
}
/**
* 标准实现
**/
double convert_to_double(int64_t i)
{
int64_t n=i>>63;
double a;
i=(i^n)-n;
if((uint64_t)i>4503599627370495LL)
{
int64_t j=(uint64_t)i>>32;
i&=0xFFFFFFFF;
a=convert52_to_double(i);
j+=0x4530000000000000LL;
a+=(*(double*)&j)-(double)(1LL<<52)*(double)(1LL<<32);
}
else
{
i+=0x4330000000000000LL;
a=(*(double*)&i)-4503599627370496.0;
}
(*(int64_t*)&a)|=(n&0x8000000000000000LL);
return a;
}
测试代码:ConsoleApplication1.cpp
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "typeconvert.h"
#include <stdio.h>
#include <xmmintrin.h>
#include <emmintrin.h>
#define PR(x,a) printf("error in "#x" %lf\n",(double)a)
void d2int32()
{
double a = (1 << 31)-0.5,b= -(1 << 31) + 0.5;
int i = (1 << 31);
while (a < b)
{
if (i != convert_to_int32(a))PR(d2int32, a);
a += 1.0;
if (i != convert_to_int32(a))PR(d2int32, a);
a += 1.0;
i+=2;
}
}
void f2int23()
{
double a = -(1 << 22) - 0.5, b = (1 << 22) + 0.5;
int i = -(1 << 22);
while (a < b)
{
if (i != convert_to_int23((float)a))PR(f2int23, a);
a += 1.0;
if (i != convert_to_int23((float)a))PR(f2int23, a);
a += 1.0;
i += 2;
}
printf("%d\n", convert_to_int23((float)b));
}
void f2int24()
{
double a = -(1 << 23) - 0.5, b = (1 << 23) + 0.5;
int i = -(1 << 23);
while (a < b)
{
if (i != convert_to_int24((float)a))PR(f2int24, a);
a += 1.0;
if (i != convert_to_int24((float)a))PR(f2int24, a);
a += 1.0;
i += 2;
}
}
void f2int32()
{
double a = (1 << 31) - 0.5, b = -(double)(1 << 31) + 0.5;
bool display = true;
while (a <= b)
{
float c = a;
if (_mm_cvtss_si32(_mm_load_ss(&c)) != convert_to_int32(c) && display) {
PR(f2int32, a);
display = false;
}
a += 1.0;
}
float c = 2147483584.5;
printf("%d\n", _mm_cvtss_si32(_mm_load_ss(&c)));//这个方法会溢出
}
void d2int52()
{
double a = -(1LL << 51) - 0.5, b = -(1LL << 51) + (1LL << 16) + 0.5;
while (a <= b)
{
if (_mm_cvtsd_si64(_mm_load_sd(&a)) != convert_to_int52(a))PR(d2int52, a);
a += 1.0;
}
a = -(1LL << 16) - 0.5, b = (1LL << 16) + 0.5;
while (a <= b)
{
if (_mm_cvtsd_si64(_mm_load_sd(&a)) != convert_to_int52(a))PR(d2int52, a);
a += 1.0;
}
a = (1LL << 51) - (1LL << 16) - 0.5, b = (1LL << 51) + 0.5;
while (a <= b)
{
if (_mm_cvtsd_si64(_mm_load_sd(&a)) != convert_to_int52(a))PR(d2int52, a);
a += 1.0;
}
printf("%lld\n", convert_to_int52(b));
}
void d2int53()
{
double a = -(1LL << 52) + 0.5, b = -(1LL << 52) + (1LL << 16) + 0.5;
while (a <= b)
{
if (_mm_cvtsd_si64(_mm_load_sd(&a)) != convert_to_int53(a))PR(d2int53, a);
a += 1.0;
}
a = -(1LL << 16) - 0.5, b = (1LL << 16) + 0.5;
while (a <= b)
{
if (_mm_cvtsd_si64(_mm_load_sd(&a)) != convert_to_int53(a))PR(d2int53, a);
a += 1.0;
}
a = (1LL << 52) - (1LL << 16) - 0.5, b = (1LL << 52);
while (a <= b)
{
if (_mm_cvtsd_si64(_mm_load_sd(&a)) != convert_to_int53(a))PR(d2int53, a);
a += 1.0;
}
}
void d2int64()
{
double a = (double)(1LL << 63) + (1LL<<10), b = (double)(1LL << 63) + (1LL << 27) + (1LL << 10);
while (a <= b)
{
if (_mm_cvtsd_si64(_mm_load_sd(&a)) != convert_to_int64(a))PR(d2int64, a);
a += (1LL << 11);
}
a = -(1LL << 16) - 0.5, b = (1LL << 16) + 0.5;
while (a <= b)
{
if (_mm_cvtsd_si64(_mm_load_sd(&a)) != convert_to_int64(a))PR(d2int64, a);
a += 1.0;
}
a = -(double)(1LL << 63) - (1LL << 27) + (1LL << 10), b = -(double)(1LL << 63) + (1LL << 10);
while (a <= b)
{
if (_mm_cvtsd_si64(_mm_load_sd(&a)) != convert_to_int64(a))PR(d2int64, a);
a += (1LL << 11);
}
printf("%lld\n", _mm_cvtsd_si64(_mm_load_sd(&b)));//这个方法会溢出
}
void f2int64()
{
printf("f2int64 true!\n");
}
void i23float()
{
for (int i = -0x400000; i <= 0x400000; i++)
{
if (convert23_to_float(i) != (float)i)
{
PR(i23float, i);
}
}
}
void i24float()
{
for (int i = -0x7FFFFF; i <= 0x7FFFFF; i++)
{
if (convert24_to_float(i) != (float)i)
{
PR(i24float, i);
}
}
}
void i32float()
{
for (int i = 0x80000000; i != 0x7FFFFFFF; i++)
{
if (convert_to_float(i) != (float)i)
{
PR(i32float, i);
}
if (i == 0x80001000)i = 0x7FFFF000;
}
}
void i64float()
{
printf("i64float true!\n");
}
void i53double()
{
for (int64_t i = -0xFFFFFFFFFFFFFLL; i != 0xFFFFFFFFFFFFFLL; i++)
{
if (convert53_to_double(i) != (double)i)
{
PR(i53double, i);
}
if (i == -0xFFFFFFFFFF000LL)i = -0xFFF;
if (i == 0xFFF)i = 0xFFFFFFFFFF000LL;
}
}
void i52double()
{
for (int64_t i = -0x8000000000000LL; i != 0x7FFFFFFFFFFFFLL; i++)
{
if (convert52_to_double(i) != (double)i)
{
PR(i52double, i);
}
if (i == -0x7FFFFFFFFF000LL)i = -0xFFF;
if (i == 0xFFF)i = 0x7FFFFFFFFF000LL;
}
}
void i32double()
{
printf("i32double true!\n");
}
void i64double()
{
for (int64_t i = -0x8000000000000000LL; i != 0x7FFFFFFFFFFFFFFFLL; i++)
{
if (convert_to_double(i) != (double)i)
{
PR(i64double, i);
}
if (i == -0x7FFFFFFFFF000000LL)i = -0xFFFFFF;
if (i == 0xFFFFFF)i = 0x7FFFFFFFFF000000LL;
}
}
int main()
{
printf("start\n");
d2int32();
printf("\n");
f2int23();
printf("\n");
f2int24();
printf("\n");
f2int32();
printf("\n");
d2int52();
printf("\n");
d2int53();
printf("\n");
d2int64();
printf("\n");
f2int64();
printf("\n");
i23float();
printf("\n");
i24float();
printf("\n");
i32float();
printf("\n");
i64float();
printf("\n");
i53double();
printf("\n");
i52double();
printf("\n");
i32double();
printf("\n");
i64double();
printf("end\n");
return 0;
}