找到2216个回复 (用户: 老虎会游泳)
  • @无名啊,这是这个函数的PHP版本,有助于理解为什么没有未定义行为:

    <?php
    function Q_rsqrt(float $number) {
        $threehalfs = 1.5;
        $x2 = $number * 0.5;
        $y = $number;
    
        $i = unpack("l", pack("f", $y))[1];
        $i = 0x5f3759df - ($i >> 1);
        $y = unpack("f", pack("l", $i))[1];
        $y = $y * ( $threehalfs - ($x2 * $y * $y) );
        $y = $y * ( $threehalfs - ($x2 * $y * $y) );
    
        return $y;
    }
    
    printf("%0.7f\n", Q_rsqrt(3.14));
    printf("%0.7f\n", Q_rsqrt(1024.0));
    printf("%0.7f\n", Q_rsqrt(10086.0));
    printf("%0.7f\n", Q_rsqrt(2147483647.0));
    
    printf("%0.14f\n", Q_rsqrt(3.14));
    printf("%0.14f\n", Q_rsqrt(1024.0));
    printf("%0.14f\n", Q_rsqrt(10086.0));
    printf("%0.14f\n", Q_rsqrt(2147483647.0));
    

    在给定的定义域和有效数字范围内,它和C版本的结果一致。如果继续增加输出的位数,结果就开始不一致了,因为PHP在内部使用64位整数和双精度浮点数,而非C代码的32位整数和单精度浮点数,只在packunpack时才转换为32位单精度,所以两者会有精度差异。

    此外32位和64位在处理符号位上可能也有差异,所以C版给出负数解的情况下PHP给出的是正数解。当然两者都是正确的解,因为负数的平方也是正数。

    Screenshot_20230127_144400.jpg(159.56 KB)

    C版本:

    #include <stdio.h>
    
    float Q_rsqrt( float number ) {
      long i;
      float x2, y;
      const float threehalfs = 1.5F;
    
      x2 = number * 0.5F;
      y  = number;
      i  = * ( long * ) &y;                       // evil floating point bit level hacking
      i  = 0x5f3759df - ( i >> 1 );               // what the fuck?
      y  = * ( float * ) &i;
      y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
      y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed
    
      return y;
    }
    
    int main() {
      printf("%0.7f\n", Q_rsqrt(3.14));
      printf("%0.7f\n", Q_rsqrt(1024.0));
      printf("%0.7f\n", Q_rsqrt(10086.0));
      printf("%0.7f\n", Q_rsqrt(2147483647.0));
    
      printf("%0.14f\n", Q_rsqrt(3.14));
      printf("%0.14f\n", Q_rsqrt(1024.0));
      printf("%0.14f\n", Q_rsqrt(10086.0));
      printf("%0.14f\n", Q_rsqrt(2147483647.0));
    
      return 0;
    }
    
  • @无名啊,此外,Q_rsqrt()函数中没有未定义行为,IEEE 754 标准已经精确的定义了单精度浮点数(float)的二进制表示,所以把它的二进制表示做为long使用不是未定义行为,结果应该是很明确的:符号位依然是符号位,指数和尾数则被拼接在一起做为整数的值。

    反向操作(把整数的二进制表示做为单精度浮点数使用)结果也很明确:符号位依然是符号位,然后接下来8位成为指数,最后23位成为尾数。

    所以,这只是一个“用户定义浮点数算法”,它与GMP等其他用户定义数学库中的自定义浮点数算法没有本质区别。代码中的每次类型转换在C中都有明确的定义。在所有使用IEEE754单精度浮点数的计算机中,结果都应该是一致的。

  • 其实无论是const,还是volatilerestrict,都是为了解决内存空间的所有权问题。

    因为C/C++可以操作原始指针,所以内存空间的所有权可以在多个线程、函数、变量之间以任意方式共享和转移,导致编译器优化很容易出问题,所以才需要这些标记加以指示。

    其他编程语言不能直接操作原始指针,所以内存空间的所有权是明确的,不需要这些编译器优化限定符。

    当然const也有语法上的含义,表明你希望编译器帮你阻止对该变量的修改,所以其他编程语言里也存在该关键字。但是volatilerestrict在语法上没有任何含义,所以在内存空间所有权明确的编程语言中完全不存在。不能对原始指针解引用的语言都是所有权明确的,带GC的语言通常属于此类。

    所谓原始指针解引用,就是类似这样的代码:

    y  = * ( float * ) &i;
    

    它在语法上提供了无限的灵活性,实际上可以用于读写任意内存地址:

    int main() {
        long i = 1;
        float y = -1;
        const int x = 12306;
    
        // 以下代码没有语法错误,可以编译通过。
    
        // 读取原始指针
        y  = * ( float * ) (&i + 10086);
        y  = * ( float * ) 10086;
    
        // 写入原始指针
        * ( float * ) (&i + 10086) = y;
        * ( float * ) 10086 = y;
    
        // 写入 const 变量
        * (int *) &x = 10010;
    
        return 0;
    }
    

    因为这种灵活性,所以在C/C++中跟踪内存空间所有权变得不可能,于是需要对所有权进行人工标记。而constvolatilerestrict正是这样的标记。

    const:我保证不写入这块内存空间。如果我通过原始指针解引用实现了写入,结果是未定义的。
    restrict:我保证不把内存空间的所有权转移给其他变量(也就是创建别名)。如果我确实转移了,结果是未定义的。
    volatile:我对该内存空间的使用不进行任何保证,请不要假设它可以被优化。至于到底能阻止哪些优化,由实现定义。

    需要说明的是:volatile不是线程同步措施,它不能提供多核CPU间的内存一致性。想实现多线程内存一致性必须使用同步原语(比如互斥锁 mutex)。

  • 一个指针经 restrict 修饰后,它(可能经过指针运算后)指向的对象不会不能有其它别名。

    并非不会,而是不能

    不会意味着编译器会阻止你为它创建别名,创建别名会导致编译错误。

    但实际上只是不能,创建别名最多产生警告,程序还是能运行,而且还可能完全无错(因为编译器优化后程序出问题只是概率事件)。

    所以,restrict体现的是你的自信,你得首先保证你的代码没有对该变量创建别名,然后才能给它加上restrict

    就像volatile,是你不自信,觉得优化这个变量会出问题,才给它加上volatile。至于不加会不会出问题,得具体问题具体分析。

  • @无名啊volatilerestrict是编译器优化指示标记,其中volatile阻止对该标识符进行优化,restrict建议编译器对该标识符进行优化。

    volatile的语义:小心,这个变量的用途很复杂,优化这个变量很可能会导致程序出问题!

    restrict的语义:我保证我只通过这个变量访问它指向的内存区域,你随便优化它,绝对不会出问题!

    这些都只是给编译器的提示,编译器不一定会遵循指示。比如,使用-O0编译时,加不加volatilerestrict参数都没有任何区别。只有-O1-O2-O3等有区别。

    对于VC++编译器,Debug模式应该体现不出区别,只有Release模式才有区别。

    const与它们不一样,它不仅是编译器优化指示标记,还进行了语法上的限制。如果不通过强制类型转换去除const标记,则无法对变量进行写入。

    不过,因为const也是编译器优化指示标记,它的语义是:我保证不会对该变量进行写入,你放心优化。所以如果后续通过强制类型转换去掉const并写入变量,则Release版程序可能会出问题。注意只是可能,编译器会尽量给出不出问题的代码,所以真想遇到问题也需要碰运气。

  • linux上古卷轴5怎么安装mod?
    110355点击 / 2023-01-02发布 / 2023-01-26回复 / /

    已经添加了一个Vortex模组管理器,亲测可以正常安装mod

    https://winegame.net/games/vortex-mod-manager/

  • 能用纯SQL实现
    要我的话肯定得用常规编程语言,甚至上人工智能

  • 求将这段js代码转成php
    86538点击 / 2023-01-18发布 / 2023-01-24回复 / /

    @张小强,这里进行了i++

    var ccc = '0c4a2013ebd12f0de2b54163fe318b1e'.charCodeAt(i++);
    
  • linux上古卷轴5怎么安装mod?
    110355点击 / 2023-01-02发布 / 2023-01-20回复 / /

    @ysyvsl,那要选的可能就是文件夹,你选skyim special endition不行吗

  • linux上古卷轴5怎么安装mod?
    110355点击 / 2023-01-02发布 / 2023-01-20回复 / /

    @ysyvsl,点“.”开头的文件夹在Linux中是隐藏文件夹,在Wine中不会显示。你可以创建一个符号连接以便能直接看到。在终端运行:

    ln -s /home/deck/.local/share/steam/steamapps /home/deck/steamapps
    

    这样Wine里就能看到steamapps文件夹了

  • linux上古卷轴5怎么安装mod?
    110355点击 / 2023-01-02发布 / 2023-01-19回复 / /

    @ysyvsl,游戏先切到英语,然后:

    Vortex安装:把汉化补丁压缩包拖到Vortex进行安装。

    或者手动安装:把汉化补丁直接解压到游戏的Data文件夹,覆盖原来的文件。

  • @晨曦,你可以说继续,然后它会继续发。

  • 求将这段js代码转成php
    86538点击 / 2023-01-18发布 / 2023-01-18回复 / /

    @胡椒舰长,我现在也登不进去了,提示”ChatGPT 现在满负荷运转“

  • 求将这段js代码转成php
    86538点击 / 2023-01-18发布 / 2023-01-18回复 / /

    @胡椒舰长,之前在用的就还能用

  • 求将这段js代码转成php
    86538点击 / 2023-01-18发布 / 2023-01-18回复 / /

    @无名啊,噗,居然是这样,ChatGPT居然犯了人类才会犯的错误,它难道也依赖某种直觉?

    图片.png(37.36 KB)

  • 求将这段js代码转成php
    86538点击 / 2023-01-18发布 / 2023-01-18回复 / /

    @幕后导演,啊我没发现,我看开头是对的就以为整个都是对的。

    从中间某个位置开始出错,情况不太妙啊,看起来出现了浮点舍入误差。

  • 求将这段js代码转成php
    86538点击 / 2023-01-18发布 / 2023-01-18回复 / /

    我觉得我应该搞一个ChatGPT机器人来自动回答此类问题

  • 求将这段js代码转成php
    86538点击 / 2023-01-18发布 / 2023-01-18回复 / /

    图片.png(141.15 KB)

    ChatGPT:

    <?php
    $obj = "A4NjFqYu5wPHsO0XTdDgMa2r1ZQocVte9UJBvk6/7=yRnhISGKblCWi+LpfE8xzm3";
    $strs = '0c4a20131ebd12f0de2b54163fe318b1e';
    $r = "";
    
    for ($i = 0; $i < strlen($strs);) {
        $ccc = ord($strs[$i++]);
        $iii = ($ccc) >> 2;
        $e = ord($strs[$i++]);
        $aaa = ((3 & $ccc) << 4) | ($e >> 4);
        $n = ord($strs[$i++]);
        $bbb = ((15 & $e) << 2) | ($n >> 6);
        $ccc = 63 & $n;
        is_nan($e) ? $bbb = $ccc = 64 : is_nan($n) && ($ccc = 64);
        $r = $r . $obj[$iii] . $obj[$aaa] . $obj[$bbb] . $obj[$ccc];
    }
    echo $r;
    ?>
    

    结果依然是sYsC1g5GsgOk16TKs61GZYMb1BMCsg1lZ6MlsgUJs2M3

  • @无名啊,嗯。现在看起来,ChatGPT更像一个设计巧妙的搜索引擎,你还是得通过关键词引导它加载对应的“记忆”,然后才能得到正确的答案。
    不过和搜索引擎相比,错误答案也被精心包装了起来,难以和正确答案做出区分。

  • 有趣的转变:

    图片.png(181.03 KB)

    看起来ChatGPT可以进行“推理”。
    或者加载英文素材库解决了中文素材库缺少蓝环章鱼资料的问题。