进行定点数学运算的最佳方法是什么? [关闭]

2023-11-24

我需要为没有 FPU 的 Nintendo DS 加速一个程序,因此我需要将浮点数学(经过仿真且速度较慢)更改为定点。

我是如何开始的,我将浮点数更改为整数,每当我需要转换它们时,我都会使用x>>8将定点变量 x 转换为实际数字并x转换为定点。很快我发现不可能跟踪需要转换的内容,而且我还意识到很难更改数字的精度(在本例中为 8)。

我的问题是,我应该如何使这变得更容易并且仍然快速?我应该创建一个FixedPoint类,还是只是一个FixedPoint8 typedef或带有一些函数/宏的结构来转换它们,或者其他什么?我应该在变量名中添加一些内容来显示它是定点的吗?


你可以尝试我的定点课程(最新可用@https://github.com/eteran/cpp-utilities)

// From: https://github.com/eteran/cpp-utilities/edit/master/Fixed.h
// See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math
/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2015 Evan Teran
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef FIXED_H_
#define FIXED_H_

#include <ostream>
#include <exception>
#include <cstddef> // for size_t
#include <cstdint>
#include <type_traits>

#include <boost/operators.hpp>

namespace numeric {

template <size_t I, size_t F>
class Fixed;

namespace detail {

// helper templates to make magic with types :)
// these allow us to determine resonable types from
// a desired size, they also let us infer the next largest type
// from a type which is nice for the division op
template <size_t T>
struct type_from_size {
    static const bool is_specialized = false;
    typedef void      value_type;
};

#if defined(__GNUC__) && defined(__x86_64__)
template <>
struct type_from_size<128> {
    static const bool           is_specialized = true;
    static const size_t         size = 128;
    typedef __int128            value_type;
    typedef unsigned __int128   unsigned_type;
    typedef __int128            signed_type;
    typedef type_from_size<256> next_size;
};
#endif

template <>
struct type_from_size<64> {
    static const bool           is_specialized = true;
    static const size_t         size = 64;
    typedef int64_t             value_type;
    typedef uint64_t            unsigned_type;
    typedef int64_t             signed_type;
    typedef type_from_size<128> next_size;
};

template <>
struct type_from_size<32> {
    static const bool          is_specialized = true;
    static const size_t        size = 32;
    typedef int32_t            value_type;
    typedef uint32_t           unsigned_type;
    typedef int32_t            signed_type;
    typedef type_from_size<64> next_size;
};

template <>
struct type_from_size<16> {
    static const bool          is_specialized = true;
    static const size_t        size = 16;
    typedef int16_t            value_type;
    typedef uint16_t           unsigned_type;
    typedef int16_t            signed_type;
    typedef type_from_size<32> next_size;
};

template <>
struct type_from_size<8> {
    static const bool          is_specialized = true;
    static const size_t        size = 8;
    typedef int8_t             value_type;
    typedef uint8_t            unsigned_type;
    typedef int8_t             signed_type;
    typedef type_from_size<16> next_size;
};

// this is to assist in adding support for non-native base
// types (for adding big-int support), this should be fine
// unless your bit-int class doesn't nicely support casting
template <class B, class N>
B next_to_base(const N& rhs) {
    return static_cast<B>(rhs);
}

struct divide_by_zero : std::exception {
};

template <size_t I, size_t F>
Fixed<I,F> divide(const Fixed<I,F> &numerator, const Fixed<I,F> &denominator, Fixed<I,F> &remainder, typename std::enable_if<type_from_size<I+F>::next_size::is_specialized>::type* = 0) {

    typedef typename Fixed<I,F>::next_type next_type;
    typedef typename Fixed<I,F>::base_type base_type;
    static const size_t fractional_bits = Fixed<I,F>::fractional_bits;

    next_type t(numerator.to_raw());
    t <<= fractional_bits;

    Fixed<I,F> quotient;

    quotient  = Fixed<I,F>::from_base(next_to_base<base_type>(t / denominator.to_raw()));
    remainder = Fixed<I,F>::from_base(next_to_base<base_type>(t % denominator.to_raw()));

    return quotient;
}

template <size_t I, size_t F>
Fixed<I,F> divide(Fixed<I,F> numerator, Fixed<I,F> denominator, Fixed<I,F> &remainder, typename std::enable_if<!type_from_size<I+F>::next_size::is_specialized>::type* = 0) {

    // NOTE(eteran): division is broken for large types :-(
    // especially when dealing with negative quantities

    typedef typename Fixed<I,F>::base_type     base_type;
    typedef typename Fixed<I,F>::unsigned_type unsigned_type;

    static const int bits = Fixed<I,F>::total_bits;

    if(denominator == 0) {
        throw divide_by_zero();
    } else {

        int sign = 0;

        Fixed<I,F> quotient;

        if(numerator < 0) {
            sign ^= 1;
            numerator = -numerator;
        }

        if(denominator < 0) {
            sign ^= 1;
            denominator = -denominator;
        }

            base_type n      = numerator.to_raw();
            base_type d      = denominator.to_raw();
            base_type x      = 1;
            base_type answer = 0;

            // egyptian division algorithm
            while((n >= d) && (((d >> (bits - 1)) & 1) == 0)) {
                x <<= 1;
                d <<= 1;
            }

            while(x != 0) {
                if(n >= d) {
                    n      -= d;
                    answer += x;
                }

                x >>= 1;
                d >>= 1;
            }

            unsigned_type l1 = n;
            unsigned_type l2 = denominator.to_raw();

            // calculate the lower bits (needs to be unsigned)
            // unfortunately for many fractions this overflows the type still :-/
            const unsigned_type lo = (static_cast<unsigned_type>(n) << F) / denominator.to_raw();

            quotient  = Fixed<I,F>::from_base((answer << F) | lo);
            remainder = n;

        if(sign) {
            quotient = -quotient;
        }

        return quotient;
    }
}

// this is the usual implementation of multiplication
template <size_t I, size_t F>
void multiply(const Fixed<I,F> &lhs, const Fixed<I,F> &rhs, Fixed<I,F> &result, typename std::enable_if<type_from_size<I+F>::next_size::is_specialized>::type* = 0) {

    typedef typename Fixed<I,F>::next_type next_type;
    typedef typename Fixed<I,F>::base_type base_type;

    static const size_t fractional_bits = Fixed<I,F>::fractional_bits;

    next_type t(static_cast<next_type>(lhs.to_raw()) * static_cast<next_type>(rhs.to_raw()));
    t >>= fractional_bits;
    result = Fixed<I,F>::from_base(next_to_base<base_type>(t));
}

// this is the fall back version we use when we don't have a next size
// it is slightly slower, but is more robust since it doesn't
// require and upgraded type
template <size_t I, size_t F>
void multiply(const Fixed<I,F> &lhs, const Fixed<I,F> &rhs, Fixed<I,F> &result, typename std::enable_if<!type_from_size<I+F>::next_size::is_specialized>::type* = 0) {

    typedef typename Fixed<I,F>::base_type base_type;

    static const size_t fractional_bits = Fixed<I,F>::fractional_bits;
    static const size_t integer_mask    = Fixed<I,F>::integer_mask;
    static const size_t fractional_mask = Fixed<I,F>::fractional_mask;

    // more costly but doesn't need a larger type
    const base_type a_hi = (lhs.to_raw() & integer_mask) >> fractional_bits;
    const base_type b_hi = (rhs.to_raw() & integer_mask) >> fractional_bits;
    const base_type a_lo = (lhs.to_raw() & fractional_mask);
    const base_type b_lo = (rhs.to_raw() & fractional_mask);

    const base_type x1 = a_hi * b_hi;
    const base_type x2 = a_hi * b_lo;
    const base_type x3 = a_lo * b_hi;
    const base_type x4 = a_lo * b_lo;

    result = Fixed<I,F>::from_base((x1 << fractional_bits) + (x3 + x2) + (x4 >> fractional_bits));

}
}

/*
 * inheriting from boost::operators enables us to be a drop in replacement for base types
 * without having to specify all the different versions of operators manually
 */
template <size_t I, size_t F>
class Fixed : boost::operators<Fixed<I,F>> {
    static_assert(detail::type_from_size<I + F>::is_specialized, "invalid combination of sizes");

public:
    static const size_t fractional_bits = F;
    static const size_t integer_bits    = I;
    static const size_t total_bits      = I + F;

    typedef detail::type_from_size<total_bits>             base_type_info;

    typedef typename base_type_info::value_type            base_type;
    typedef typename base_type_info::next_size::value_type next_type;
    typedef typename base_type_info::unsigned_type         unsigned_type;

public:
    static const size_t base_size          = base_type_info::size;
    static const base_type fractional_mask = ~((~base_type(0)) << fractional_bits);
    static const base_type integer_mask    = ~fractional_mask;

public:
    static const base_type one = base_type(1) << fractional_bits;

public: // constructors
    Fixed() : data_(0) {
    }

    Fixed(long n) : data_(base_type(n) << fractional_bits) {
        // TODO(eteran): assert in range!
    }

    Fixed(unsigned long n) : data_(base_type(n) << fractional_bits) {
        // TODO(eteran): assert in range!
    }

    Fixed(int n) : data_(base_type(n) << fractional_bits) {
        // TODO(eteran): assert in range!
    }

    Fixed(unsigned int n) : data_(base_type(n) << fractional_bits) {
        // TODO(eteran): assert in range!
    }

    Fixed(float n) : data_(static_cast<base_type>(n * one)) {
        // TODO(eteran): assert in range!
    }

    Fixed(double n) : data_(static_cast<base_type>(n * one))  {
        // TODO(eteran): assert in range!
    }

    Fixed(const Fixed &o) : data_(o.data_) {
    }

    Fixed& operator=(const Fixed &o) {
        data_ = o.data_;
        return *this;
    }

private:
    // this makes it simpler to create a fixed point object from
    // a native type without scaling
    // use "Fixed::from_base" in order to perform this.
    struct NoScale {};

    Fixed(base_type n, const NoScale &) : data_(n) {
    }

public:
    static Fixed from_base(base_type n) {
        return Fixed(n, NoScale());
    }

public: // comparison operators
    bool operator==(const Fixed &o) const {
        return data_ == o.data_;
    }

    bool operator<(const Fixed &o) const {
        return data_ < o.data_;
    }

public: // unary operators
    bool operator!() const {
        return !data_;
    }

    Fixed operator~() const {
        Fixed t(*this);
        t.data_ = ~t.data_;
        return t;
    }

    Fixed operator-() const {
        Fixed t(*this);
        t.data_ = -t.data_;
        return t;
    }

    Fixed operator+() const {
        return *this;
    }

    Fixed& operator++() {
        data_ += one;
        return *this;
    }

    Fixed& operator--() {
        data_ -= one;
        return *this;
    }

public: // basic math operators
    Fixed& operator+=(const Fixed &n) {
        data_ += n.data_;
        return *this;
    }

    Fixed& operator-=(const Fixed &n) {
        data_ -= n.data_;
        return *this;
    }

    Fixed& operator&=(const Fixed &n) {
        data_ &= n.data_;
        return *this;
    }

    Fixed& operator|=(const Fixed &n) {
        data_ |= n.data_;
        return *this;
    }

    Fixed& operator^=(const Fixed &n) {
        data_ ^= n.data_;
        return *this;
    }

    Fixed& operator*=(const Fixed &n) {
        detail::multiply(*this, n, *this);
        return *this;
    }

    Fixed& operator/=(const Fixed &n) {
        Fixed temp;
        *this = detail::divide(*this, n, temp);
        return *this;
    }

    Fixed& operator>>=(const Fixed &n) {
        data_ >>= n.to_int();
        return *this;
    }

    Fixed& operator<<=(const Fixed &n) {
        data_ <<= n.to_int();
        return *this;
    }

public: // conversion to basic types
    int to_int() const {
        return (data_ & integer_mask) >> fractional_bits;
    }

    unsigned int to_uint() const {
        return (data_ & integer_mask) >> fractional_bits;
    }

    float to_float() const {
        return static_cast<float>(data_) / Fixed::one;
    }

    double to_double() const        {
        return static_cast<double>(data_) / Fixed::one;
    }

    base_type to_raw() const {
        return data_;
    }

public:
    void swap(Fixed &rhs) {
        using std::swap;
        swap(data_, rhs.data_);
    }

public:
    base_type data_;
};

// if we have the same fractional portion, but differing integer portions, we trivially upgrade the smaller type
template <size_t I1, size_t I2, size_t F>
typename std::conditional<I1 >= I2, Fixed<I1,F>, Fixed<I2,F>>::type operator+(const Fixed<I1,F> &lhs, const Fixed<I2,F> &rhs) {

    typedef typename std::conditional<
        I1 >= I2,
        Fixed<I1,F>,
        Fixed<I2,F>
    >::type T;

    const T l = T::from_base(lhs.to_raw());
    const T r = T::from_base(rhs.to_raw());
    return l + r;
}

template <size_t I1, size_t I2, size_t F>
typename std::conditional<I1 >= I2, Fixed<I1,F>, Fixed<I2,F>>::type operator-(const Fixed<I1,F> &lhs, const Fixed<I2,F> &rhs) {

    typedef typename std::conditional<
        I1 >= I2,
        Fixed<I1,F>,
        Fixed<I2,F>
    >::type T;

    const T l = T::from_base(lhs.to_raw());
    const T r = T::from_base(rhs.to_raw());
    return l - r;
}

template <size_t I1, size_t I2, size_t F>
typename std::conditional<I1 >= I2, Fixed<I1,F>, Fixed<I2,F>>::type operator*(const Fixed<I1,F> &lhs, const Fixed<I2,F> &rhs) {

    typedef typename std::conditional<
        I1 >= I2,
        Fixed<I1,F>,
        Fixed<I2,F>
    >::type T;

    const T l = T::from_base(lhs.to_raw());
    const T r = T::from_base(rhs.to_raw());
    return l * r;
}

template <size_t I1, size_t I2, size_t F>
typename std::conditional<I1 >= I2, Fixed<I1,F>, Fixed<I2,F>>::type operator/(const Fixed<I1,F> &lhs, const Fixed<I2,F> &rhs) {

    typedef typename std::conditional<
        I1 >= I2,
        Fixed<I1,F>,
        Fixed<I2,F>
    >::type T;

    const T l = T::from_base(lhs.to_raw());
    const T r = T::from_base(rhs.to_raw());
    return l / r;
}

template <size_t I, size_t F>
std::ostream &operator<<(std::ostream &os, const Fixed<I,F> &f) {
    os << f.to_double();
    return os;
}

template <size_t I, size_t F>
const size_t Fixed<I,F>::fractional_bits;

template <size_t I, size_t F>
const size_t Fixed<I,F>::integer_bits;

template <size_t I, size_t F>
const size_t Fixed<I,F>::total_bits;

}

#endif

它被设计为几乎可以替代浮点数/双精度数,并且具有可选择的精度。它确实利用 boost 来添加所有必要的数学运算符重载,因此您也需要它(我相信为此它只是一个标头依赖项,而不是库依赖项)。

顺便说一句,常见用法可能是这样的:

using namespace numeric;
typedef Fixed<16, 16> fixed;
fixed f;

唯一真正的规则是该数字必须加起来等于系统的本机大小,例如 8、16、32、64。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

进行定点数学运算的最佳方法是什么? [关闭] 的相关文章

随机推荐

  • 为什么将 ArrayList 的泛型转换为超类不起作用?

    有人可以向我解释一下为什么标记该行吗 this line gives a compile error why 在下面的代码示例中不起作用 import java util ArrayList public class GenericCast
  • NLTK 正则表达式标记生成器在正则表达式中不能很好地处理小数点

    我正在尝试编写一个文本规范化器 需要处理的基本情况之一是像3 14 to three point one four or three point fourteen 我目前正在使用该模式 d d with nltk regexp tokeni
  • 查找两个字符串之间的相似度度量

    在Python中如何获得一个字符串与另一个字符串相似的概率 我想要得到一个十进制值 如 0 9 意味着 90 等 最好使用标准 Python 和库 e g similar Apple Appel would have a high prob
  • 使用 Ruby 将大文件上传到 S3 失败并出现内存不足错误,如何分块读取和上传?

    我们通过 Ruby AWS SDK v2 从 Windows 计算机将各种文件上传到 S3 我们已经使用 Ruby 1 9 进行了测试 我们的代码工作正常 除非遇到大文件 抛出内存不足错误 首先 我们使用以下代码将整个文件读入内存 body
  • 是否有用于双精度倒数平方根的快速 C 或 C++ 标准库函数?

    我发现自己打字 double foo 1 0 sqrt 很多 而且我听说现代处理器具有内置的反平方根操作码 是否有 C 或 C 标准库的反平方根函数 使用双精度浮点数 准确度为1 0 sqrt 与结果一样快或更快1 0 sqrt 不 不 没
  • 在经典 ASP 中对集合进行排序

    这是一个非常简单的问题 如何对集合进行排序 我有一个 CSV 文件 其中的行按随机顺序排列 我想根据一列中的日期对行进行排序 我是否将行添加到记录集中 我可以使用 Scripting Dictionary 进行排序吗 显然我已经被 NET
  • 字体大小的默认单位?

    网上的各种文本声称 pt 是默认的字体大小单位 当没有提供时 但是 我自己的测试似乎证明并非如此 我读过许多关于 W3C 的文档 涵盖 CSS 1 3 的字体大小 但我似乎无法在任何规范中找到对默认单位的实际引用 我在 Chrome 和 I
  • 阻止 PHP 解析非 PHP 文件,例如 someFile.php.txt

    我刚刚安装了 phpdocumentor 但收到了奇怪的错误 我终于找到了问题所在 Phpdocumentor 创建各种文件 例如 someFile php txt 其中包含 PHP 代码 但不打算进行解析 事实证明 我的服务器正在解析它们
  • 如何更改 Bootstrap 4 上的导航栏悬停颜色?

    我需要将导航栏悬停颜色更改为其他颜色 我设法自己更改导航栏文本颜色 但在检查元素的悬停颜色中找不到要更改的正确颜色 然后我在堆栈溢出上查找了以前的答案 但它们对我的代码不起作用 任何投入将不胜感激
  • C 函数调用中的默认参数提升

    Setup 我对在 C 中调用函数时的默认参数提升有几个问题 这是第 6 5 2 2 节 函数调用 第 6 7 和 8 段C99 标准 pdf 为了便于阅读 添加了重点并分成列表 第 6 段 如果表示被调用函数的表达式的类型为不包括原型 对
  • Double 到 String 保持尾随零

    我尝试将双精度值转换为字符串并使用Replace 方法 将 替换为 这很好用 但只有当尾随数字不为零时 我的字符串中才需要零 即使该值为 1234 0 0 这对于十进制值效果很好 我尝试将双精度数转换为十进制数 但如果有零 我会丢失小数位
  • 调用 CallVoidMethod 时 JNI 崩溃

    我正在尝试从 Android 应用程序中的本机 C 代码调用 java 方法 使用 JNI 听起来很简单 但我的代码在最终调用方法本身时总是崩溃 这是我的代码 本机 C 代码 JNIEXPORT void JNICALL Java com
  • 如何更改 XAMPP 中 PHP 的默认路径?

    我正在使用 xampp 来部署 Web 应用程序 它将 PHP 模块包含在一个包中 现在我想做的是更改 PHP 的默认路径 以便我可以使用其他版本的 PHP 而无需覆盖现有模块 我的新 PHP 副本存在于桌面上 如何配置 Apache 以引
  • 将 JPanel 上的组件置于前面 (Java)

    在VB中 您可以使用zOrder 在 Net中 它是 SetChildIndex 在你问之前 不 在这种情况下我没有使用布局管理器 如果您有两个叠在一起的组件 那么在它们已经显示之后如何更改顺序 由于空间不足 我有一个按钮稍微重叠在另一个组
  • 与 Windows 10 相比,Android 模拟器在 ubuntu 17.04 上运行速度极慢

    我尝试从此链接安装 kvm https help ubuntu com community KVM Installation 但即使在尝试此操作之后 模拟器在软件 GLES 2 0 模式下运行时仍然很慢 并且当我选择硬件 GLES 2 0
  • (默认)为每个可变参数类型构造一个对象

    考虑这个代码片段 void Foo std string str1 std string str2 template
  • IE10 setInterval 内存泄漏的解决方法

    在测试我们的 Javascript 库期间 我认为我们在 IE10 v10 0 9200 16519 Windows 8 64 位 Javascript 实现中发现了严重的内存泄漏setInterval 一个简单的测试用例表明 如果在函数的
  • SQL - 源代码控制和架构/脚本管理

    我的公司刚刚完成年度审核流程 我终于说服他们 是时候找到更好的解决方案来管理我们的 SQL 模式 脚本了 目前 我们只有几个脚本需要手动更新 我曾在另一家公司使用过 VS2008 数据库版本 这是一个很棒的产品 我的老板让我看一下 Redg
  • “git submodule foreach git pull origin master”和“git pull origin master --recurse-submodules”有什么区别

    我有一个 dotfiles 存储库 其中所有 vim 插件都存储为子模块 因此在发生更改时很容易更新 我以为这两个命令做了同样的事情 但我注意到事实并非如此 我知道我有几个子模块需要更新 所以我跑了git pull origin maste
  • 进行定点数学运算的最佳方法是什么? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心以获得指导 我需要为没有 FPU 的