以最小的精度损失将 float 转换为 short [关闭]
Posted
技术标签:
【中文标题】以最小的精度损失将 float 转换为 short [关闭]【英文标题】:convert float to short with minimal loss of precision [closed] 【发布时间】:2014-08-08 10:23:08 【问题描述】:我有这个生成浮点值(例如 0.37885)的正弦波,但我希望它们作为短裤。用 short 直接转换给我的值为 0。那么解决方案是什么?
谁能告诉我怎么做 - 理想情况下不损失精度 - 或者如果这是可能的,那么精度损失最小?
【问题讨论】:
float
的大小通常为 4 个字节,short
的大小通常为 2 个字节。在将 4 个字节的数据压缩成 2 个字节的数据时,您究竟希望如何避免精度损失?
为了帮助理解你试图解决的问题,你能解释一下为什么你需要在 short 中存储一个浮点数吗?
另外,你打算如何将浮点值“转换”为非浮点值?
您需要决定将某个点用于最低位,或者像 FPU 通常那样编写一些代码来存储float
。
@BackSlash:这不是问题。如果您采用相同大小的整数(int
在大多数平台上都可以),那么数据类型就没有问题。例如,给定float f
和int i
,您可以使用i = *(int*)&f
。
【参考方案1】:
public static short floatToShort(float x)
if (x < Short.MIN_VALUE)
return Short.MIN_VALUE;
if (x > Short.MAX_VALUE)
return Short.MAX_VALUE;
return (short) Math.round(x);
你会丢失小数部分:
float 4 byte floating-point
double 8 byte floating-point (normal)
short 2 byte integer
int 4 byte integer (normal)
long 8 byte integer
编辑:
也许您想知道如何将 float(4 个字节)的位保存到 int(4 个字节)中: (http://docs.oracle.com/javase/7/docs/api/java/lang/Float.html#floatToRawIntBits(float))
float x = 0.1f;
int n = Float.floatToRawIntBits(x);
float y = Float.intBitsToFloat(n);
【讨论】:
我同意会有精度损失,但我想要的只是一种解决方法。我会将其标记为答案(如果允许的话)。 感谢保存位方法。这会很有帮助。【参考方案2】:原则上,您可以将其乘以 100000,将其转换为 int,然后减去 -32,767 并将其转换为 short。如果您的所有值仍然在 -32,767 到 32,767 范围内,那可能是您能做的最好的事情。否则,您将不得不限制您的精度并乘以 10000。
当然,当你使用 short 时,你必须记住将其分割回去。
【讨论】:
我认为这是 only 正确答案,但假设他的正弦波返回值介于 -1 和 1(含)之间,我会将它们乘以 65535,然后减去 32767 . 这行不通。例如 1 的值:1 * 65535 - 32767 = 32768。这是错误的,它超出了短范围。其他示例:-1 * 65535 - 32767 将给出 -98302。【参考方案3】:如果您输入的 float
值在定义的范围内(现在假设它们在 -1..1
的范围内,不包括在内),您可以将它们相乘以获得您将丢弃的分数的值。
有效的短范围是:-32768..32767
,因此您可以在这种情况下与32768
进行倍数(最大短/最大输入值)。
例如:
float f = 0.23451f;
short s = (short) (f * 32768);
要将short
值解码为float
:
float f2 = s / 32768f;
【讨论】:
【参考方案4】:short
是一个integral type,所以它只能包含整数。在short
中0.37885
的唯一两个选择是0
或1
,这两者(在我看来)都失去了相当多的精度。
所以答案是:如果您可以丢失所有小数值,请使用强制转换 Float#shortValue
或 Math.round(float)
(并将生成的 int
强制转换为 short
)。
示例:Live Copy
float f1 = 0.37885f;
short s1 = (short)Math.round(f1);
System.out.println("s1 = " + s1);
float f2 = 27.67885f;
short s2 = (short)Math.round(f2);
System.out.println("s2 = " + s2);
输出:
s1 = 0 s2 = 28在你说的评论中:
我有这个正弦波,它会生成上面提到的值,但我希望它们作为短裤。
啊,现在,我们可以用它做点什么了。大概你得到的值都在0
和1
之间。您可以通过乘法将它们存储为短裤。由于short
的范围是 -32,768 到 37,767,因此乘以它们的方便数字可能是 10000:
short s = Math.round(floatValue * 10000);
我们为您的示例获得的号码是3789
。示例:Live Copy
float floatValue = 0.37885f;
short s = (short)Math.round((double)floatValue * 10000);
System.out.println("s = " + s);
这不是相同的值,当然,它是乘以一万的值,所以你要在任何地方使用它,你必须允许它。 p>
【讨论】:
为什么这是个问题?如果您采用相同大小的整数(int
在大多数平台上都可以),那么数据类型就没有问题。例如,给定float f
和int i
,您可以使用i = *(int*)&f
。
@barakmanos 那是什么&
?
@barakmanos:这是 Java,而不是 C。您可能也可以在 Java 中做类似的事情,但是 A) float
和 short
不是大小相同,并且 B)极OP 不太可能想要组成float
的位的short
(或int
)视图。 :-)
哎呀,对不起(@BackSlash - 向你道歉)...... :)
@barakmanos 很有趣,你知道它只是存储位,但数学值完全不同。 Java有Float.floatToRawIntBits以上是关于以最小的精度损失将 float 转换为 short [关闭]的主要内容,如果未能解决你的问题,请参考以下文章