文档章节

Unity net平台的float精度问题

梦想游戏人
 梦想游戏人
发布于 2017/09/04 20:48
字数 923
阅读 97
收藏 0

帧同步的话,这个问题尤为突出,可用定点数来替代浮点数解决

 

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的运算速度 并不是很快

 

© 著作权归作者所有

共有 人打赏支持
上一篇: Unity加密方案
梦想游戏人
粉丝 35
博文 435
码字总数 123998
作品 0
成都
私信 提问
【Unity】多线程和主线程交互使用类——Loom工具分享(转载)

作者:D.S.Qiu 原文:Unity多线程(Thread)和主线程(MainThread)交互使用类——Loom工具分享 熟悉Unity的developer都知道在Unity中的线程不能使用Unity的对象,但可以使用Unity的值类型变...

何三思
04/12
0
0
《杜增强讲Unity之Tanks坦克大战》4-坦克的移动和旋转

4 坦克移动和旋转 本节课的目标是实现同时wsad和上下左右控制两个坦克分别移动和旋转 4.1 本节代码预览 image 将上节课场景s2另存为s3. 4.2 添加车轮扬沙效果 从Prefabs里面找到DustTra...

杜增强
10/22
0
0
Mock的基本概念和方法(续)

本博客(http://blog.csdn.net/livelylittlefish )贴出作者(三二一@小鱼)相关研究、学习内容所做的笔记,欢迎广大朋友指正! Content 0. 序 1. 平台 2. 第三方库 3. 如何使用CMock和Unity...

晨曦之光
2012/03/09
1K
0
《杜增强讲Unity之Tanks坦克大战》9-发射子弹时蓄力

9 发射子弹时蓄力 实现效果如下 image 按下开火键(坦克1为空格键)重置力为最小力,一直按着的时候蓄力,抬起的时候发射。如果按着的时候蓄力到最大,则自动发射,此时在抬起则不会重复发射。...

杜增强
10/22
0
0
继 Mac/Windows 版后,Unity 发布 Linux 内测版

继上个月 Unity 宣布支持 Linux 版本的计划后,今天(8月26日)Unity 的 Linux 内测版本终于发布了。这个版本基于Unity 5.1.0f3版本,目前处于内测阶段。在试用反馈的基础上,Linux版本会和M...

文刚的技术博客
2015/08/28
9.8K
26

没有更多内容

加载失败,请刷新页面

加载更多

Spring Cloud Alibaba Sentinel 整合 Feign 的设计实现

作者 | Spring Cloud Alibaba 高级开发工程师洛夜 来自公众号阿里巴巴中间件投稿 前段时间 Hystrix 宣布不再维护之后(Hystrix 停止开发。。。Spring Cloud 何去何从?),Feign 作为一个跟 ...

Java技术栈
11分钟前
3
0
虚拟机加密

在超融合的基础设施和虚拟化成为常态的世界里,对加密的要求越来越高,越来越迫切,IT部门需考虑的重大安全问题和方法也浮现了出来。 物理数据中心时代,采取双保险式数据安全方法是相对简单...

linuxCool
15分钟前
0
0
MySQL 主从同步

MySQL主从介绍 MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主从后,在A上写数据,另外一台B也会跟着写数据,两者数据实时同步的 MySQL主从是基于binlog的,主上须开启bin...

野雪球
27分钟前
0
0
OSChina 周一乱弹 —— 温柔的人应该这样

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @clouddyy :#每日一歌# 《フィクション-sumika》 《フィクション-sumika》 手机党少年们想听歌,请使劲儿戳(这里) 假期时间干嘛去, @for...

小小编辑
今天
202
7
[LintCode] Serialize and Deserialize Binary Tree(二叉树的序列化和反序列化)

描述 设计一个算法,并编写代码来序列化和反序列化二叉树。将树写入一个文件被称为“序列化”,读取文件后重建同样的二叉树被称为“反序列化”。 如何反序列化或序列化二叉树是没有限制的,你...

honeymose
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部