【发布时间】:2021-12-04 15:15:15
【问题描述】:
我有一个程序可以接收 20,000 到 500,000 个速度向量,并且必须输出这些向量乘以某个标量。该程序允许用户设置可变精度,这基本上就是计算中要截断的小数位数。程序现在很慢,我发现这不是因为乘了很多数字,而是因为我使用截断浮点值的方法。
我已经在此处查看了几种截断小数的解决方案,like this one,他们大多推荐使用 DecimalFormat。这对于格式化小数一次或两次以打印出漂亮的用户输出非常有效,但对于需要在几秒钟内发生的数十万次截断来说太慢了。
将浮点值截断为 n 个位置的最有效方法是什么,同时将执行时间保持在最高优先级?我不关心资源使用、约定或外部库的使用。只要能以最快的速度完成工作。
编辑:对不起,我想我应该更清楚。这是我试图说明的一个非常简化的版本:
import java.util.*;
import java.lang.*;
import java.text.DecimalFormat;
import java.math.RoundingMode;
public class MyClass {
static class Vector{
float x, y, z;
@Override
public String toString(){
return "[" + x + ", " + y + ", " + z + "]";
}
}
public static ArrayList<Vector> generateRandomVecs(){
ArrayList<Vector> vecs = new ArrayList<>();
Random rand = new Random();
for(int i = 0; i < 500000; i++){
Vector v = new Vector();
v.x = rand.nextFloat() * 10;
v.y = rand.nextFloat() * 10;
v.z = rand.nextFloat() * 10;
vecs.add(v);
}
return vecs;
}
public static void main(String args[]) {
int precision = 2;
float scalarToMultiplyBy = 4.0f;
ArrayList<Vector> velocities = generateRandomVecs();
System.out.println("First 10 raw vectors:");
for(int i = 0; i < 10; i++){
System.out.print(velocities.get(i) + " ");
}
/*
This is the code that I am concerned about
*/
DecimalFormat df = new DecimalFormat("##.##");
df.setRoundingMode(RoundingMode.DOWN);
long start = System.currentTimeMillis();
for(Vector v : velocities){
/* Highly inefficient way of truncating*/
v.x = Float.parseFloat(df.format(v.x * scalarToMultiplyBy));
v.y = Float.parseFloat(df.format(v.y * scalarToMultiplyBy));
v.z = Float.parseFloat(df.format(v.z * scalarToMultiplyBy));
}
long finish = System.currentTimeMillis();
long timeElapsed = finish - start;
System.out.println();
System.out.println("Runtime: " + timeElapsed + " ms");
System.out.println("First 10 multiplied and truncated vectors:");
for(int i = 0; i < 10; i++){
System.out.print(velocities.get(i) + " ");
}
}
}
这样做非常重要的原因是程序的不同部分会将三角函数值存储在查找表中。查找表将预先生成 n 个位置,因此任何具有 7 个位置(即 5.2387471)的浮点值的速度向量必须在查找前截断到 n 个位置。需要截断而不是舍入,因为在这个程序的上下文中,如果一个向量略小于它的真实值,但不大于它是可以的。
Lookup table for 2 decimal places:
...
8.03 -> -0.17511085919
8.04 -> -0.18494742685
8.05 -> -0.19476549993
8.06 -> -0.20456409661
8.07 -> -0.21434223706
...
假设我想查找上表中向量{8.040844, 8.05813164, 8.065688} 中每个元素的余弦值。显然,我不能直接查找这些值,但可以在表格中查找{8.04, 8.05, 8.06}。
我需要的是一种从{8.040844, 8.05813164, 8.065688} 到{8.04, 8.05, 8.06} 的非常快速的方法
【问题讨论】:
-
您需要向我们展示您现有的代码并解释您如何确定问题出在截断。没有这个,我们只是猜测。请(重新)阅读How to Ask。
-
没有将浮点数舍入或截断到特定小数位数的事情。 FP有二进制位,没有小数位,两者不可通约。我建议您尽可能精确地进行四舍五入,直到 display 或 print 以 base-10 为单位的值,此时舍入或截断变得微不足道你愿意。
-
如果这是一个独立的程序,我认为它“目前非常慢”的真正原因是多种因素的结合。其他因素将/可能包括读取和写入数据的开销,以及 JVM 启动/预热开销。您是否分析您的代码以确定瓶颈的真正位置? (或者你的“发现”实际上只是猜测截断是瓶颈?)
-
不要尝试使用浮点数作为查找表中的键(除非您乐于进行二进制搜索,在这种情况下不需要截断)使用整数,并且在您进行查找,将数字乘以 100 并截断为整数。
-
Re“……如果一个向量略小于它的真实值,但不大于它是可以的”:这是可疑的。您显示的示例余弦条目是经过原点的几个周期;波浪起伏了好几次。小于理想数的计算数可能会产生小于或大于理想余弦的余弦,具体取决于它在循环中的位置。这指向一个 X-Y 问题:您有一些先前的问题要解决,已经决定了一个解决方案,并且正在询问我们如何实施该解决方案。但这不是一个好的解决方案;你应该重新考虑原来的问题。
标签: java floating-point