帧同步的话,这个问题尤为突出,可用定点数来替代浮点数解决
1.....可以自己实现一套,简单的方案,就是全程用long来运算在需要转换的时候才转换
基本思想如下,在数值运算时,直接long和long 之间的运算,如果比如long和float运算,那么用函数Parse转换为long再运算,这种效率相对Fix64那种方法速度更快,并且具备可读性,在这里应该严格控制逻辑的输入和运算都是long(int64),只有在输出需要其他数据类型才转换,比如UI显示,输出到unity。。。在这个简易的定点数算法里面,非常容易溢出,原因在于用于表示小数部分的数字在参与进来的时候容易溢出,相同的数量级的数子,之所以float不会溢出是因为其用的科学计数法,而这个简易的并没有用科学计数法来表示。
public struct Decimal
{
public const long MaxValue = long.MaxValue;
public const long MinValue = long.MinValue;
//转换规则,用大精度转换为小精度,比如float就用double来转换
//对于乘除来说, 他们之间并不能直接相除 long_a*long_a / split_xxx 才是真正的结果
public const long SPLIT_LONG = 100000;
public const int SPLIT_INT = 100000;
public const float SPLIT_FLOAT = 100000.0f;
public const double SPLIT_DOUBLE = 100000.0;
public long value;
public Decimal(Decimal other)
{
this.value = other.value;
}
public Decimal(long value)
{
this.value = value;// 默认构造不认为是定点 需要手动调用parse
}
public Decimal(double value)
{
this.value = Parse(value);
}
public Decimal(float value)
{
this.value = Parse(value);
}
public Decimal(int value)
{
this.value = Parse(value);
}
public static implicit operator Decimal(float value)
{
return new Decimal(value);
}
public static implicit operator Decimal(double value)
{
return new Decimal(value);
}
public static implicit operator Decimal(int value)
{
return new Decimal(value);
}
public static implicit operator Decimal(long value)
{
return new Decimal(value);
}
public static long Parse(float v)
{
double tmp = v;
return (long)(tmp * SPLIT_DOUBLE);
}
public static long Parse(int v)
{
long tmp = v;
return (long)(tmp * SPLIT_INT);
}
public static long Parse(double v)
{
double tmp = v;
return (long)(tmp * SPLIT_DOUBLE);
}
public static double ToDouble(long v)
{
return (double)v / SPLIT_DOUBLE;
}
public static int ToInt(long v)
{
return (int)(v / SPLIT_LONG);
}
public static double ToFloat(long v)
{
return (float)ToDouble(v);
}
public static double ToDouble(Decimal v)
{
return (double)v.value / SPLIT_DOUBLE;
}
public static int ToInt(Decimal v)
{
return (int)(v.value / SPLIT_LONG);
}
public static double ToFloat(Decimal v)
{
return (float)ToDouble(v);
}
public static Decimal operator *(Decimal x, Decimal y)
{
}
}
2.....另外也有一个现成的 用long 的定点数库 Fix64
https://github.com/jjcat/FixedMath.Net/blob/master/Fix64.cs
核心算法是,用末尾32位表示小数部分
该算法的rawvalue并不直接可读,虽然是struct分配是在stack但是速度依然比较慢,可以看做是用long来实现的科学计数法,
long xxxx=0 ;
const int FRACTIONAL_PLACES = 32;
const long ONE = 1L << FRACTIONAL_PLACES;
xxxx += ONE * 5;
xxxx += ONE * 2;
Debug.LogError((float)xxxx / ONE + " " + ONE);
输出7
以下是对相同数据量运算的简单测试
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using FixMath.NET;
using System.Runtime.InteropServices;
public class NewBehaviourScript : MonoBehaviour
{
// Use this for initialization
void Start()
{
// this.GetComponent<Animator>().Play("npc_ani_100011_attack_01");
// this.GetComponent<Animator>().Play("npc_ani_100011_standby_01");
long xx = 2;
decimal bb = 2;
float ff = 2f;
Fix64 fx = (Fix64)2f;
long dd = 2 * Decimal.SPLIT_LONG;
System.Diagnostics.Stopwatch aa = new System.Diagnostics.Stopwatch();
aa.Stop();
aa.Reset();
aa.Start();
for (int i = 0; i < 999999; i++)
{
xx *= 2;
xx /= 2;
}
Debug.LogError("long time=" + aa.ElapsedMilliseconds + " " + xx);
aa.Stop();
aa.Reset();
aa.Start();
for (int i = 0; i < 999999; i++)
{
bb *= 2;
bb /= 2;
}
Debug.LogError("decimal time=" + aa.ElapsedMilliseconds);
aa.Stop();
aa.Reset();
aa.Start();
for (int i = 0; i < 999999; i++)
{
ff *= 2f;
ff /= 2f;
}
Debug.LogError("float time=" + aa.ElapsedMilliseconds + " " + ff);
aa.Stop();
aa.Reset();
aa.Start();
for (int i = 0; i < 999999; i++)
{
fx = Fix64.FastMul((Fix64)2f, fx);
fx /= (Fix64)2f;
}
Debug.LogError("fix64 with float time=" + aa.ElapsedMilliseconds);
Fix64 twof = (Fix64)2f;
aa.Stop();
aa.Reset();
aa.Start();
for (int i = 0; i < 999999; i++)
{
fx = Fix64.FastMul(twof, fx);
fx /= twof;
}
Debug.LogError("fix64 time=" + aa.ElapsedMilliseconds);
aa.Stop();
aa.Reset();
aa.Start();
for (int i = 0; i < 999999; i++)
{
dd *= Decimal.Parse(2f);
dd /= Decimal.Parse(2f);
}
Debug.LogError("light decimal time=" + aa.ElapsedMilliseconds + " " + Decimal.ToDouble(dd));
/* long xxxx=0 ;
const int FRACTIONAL_PLACES = 16;
const long ONE = 1L << FRACTIONAL_PLACES;
xxxx += ONE * 5;
xxxx += ONE * 2;
Debug.LogError((float)xxxx / ONE + " " + ONE + " " + ONE*ONE/ONE);*/
}
// Update is called once per frame
void Update()
{
}
}
可见Fix64的运算速度 并不是很快