#44 不简单的绝对值

2021-09-17

这篇文章讲到了绝对值计算的问题:One does not simply calculate the absolute value

IEEE 754

三个特殊值:

  1. 如果指数是0并且尾数的小数部分是0,这个数 ±0(和符号位相关)
  2. 如果指数 = 2^e - 1 并且尾数的小数部分是 0,这个数是 ±∞(同样和符号位相关)
  3. 如果指数 = 2^e - 1 并且尾数的小数部分非 0,这个数表示为非数(NaN)。

abs 的实现

class Test {
    public static double abs(double value) {
        if (value < 0) {
            return -value;
        }
        return value;
    }
    public static void main(String[] args) {
        double x = -0.0;
        if (1 / abs(x) < 0) {
            System.out.println("oops");
        }
    }
}

if 中加上条件:value == -0.0 是行不通的,因为 +0.0 == -0.0,可以使用 JDK 中的 Double.compare:

public static double abs(double value) {
    if (value < 0 || Double.compare(value, -0.0) == 0) {
        return -value;
    }
    return value;
}

这样确实有效,不过效率上可能会受到影响,abs 的复杂性就上了一个台阶。

JDK 17 中的实现

java/lang/Double.java

public static int compare(double d1, double d2) {
    if (d1 < d2)
        return -1;           // Neither val is NaN, thisVal is smaller
    if (d1 > d2)
        return 1;            // Neither val is NaN, thisVal is larger

    // Cannot use doubleToRawLongBits because of possibility of NaNs.
    long thisBits    = Double.doubleToLongBits(d1);
    long anotherBits = Double.doubleToLongBits(d2);

    return (thisBits == anotherBits ?  0 : // Values are equal
            (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
             1));                          // (0.0, -0.0) or (NaN, !NaN)
}

重新实现

参考 JDK 中的实现,重写 abs

private static final long MINUS_ZERO_LONG_BITS = Double.doubleToLongBits(-0.0);
public static double abs(double value) {
    if (value < 0 || Double.doubleToLongBits(value) == MINUS_ZERO_LONG_BITS) {
        return -value;
    }
    return value;
}

新的问题:NaN 的处理,处理方法:把 doubleToLongBits 改成 doubleToRawLongBits

private static final long MINUS_ZERO_LONG_BITS = Double.doubleToRawLongBits(-0.0);
public static double abs(double value) {
    if (value < 0 || Double.doubleToRawLongBits(value) == MINUS_ZERO_LONG_BITS) {
        return -value;
    }
    return value;
}

JVM 的 JIT 会替换这次调用为底层的 CPU 寄存器操作,效率非常可观。

PS:如果可以省去这个分支的判断逻辑,JVM 可以给我们更好的性能优化?

  1. 中间涉及 CPU 分支预测(branch predictor),如果预测错误,可能会付出相对昂贵的代码。

    We know that branches are bad. If the CPU branch predictor guesses incorrectly, they can be very expensive.

  2. 有传言说,这个调用(doubleToRawLongBits)会导致浮点数寄存器转换到通用集成器。

    Although there are rumors saying that this call may still lead to a transfer from a floating-point register to a general-purpose register. Still it's very fast.

进一步优化

采用 0 减负数等于正数,并且 0 - -0 = 0 的规则:

public static double abs(double value) {
    if (value <= 0) {
        return 0.0 - value;
    }
    return value;
}

这就是长期以来(直到最新的 Java 17),JDK 使用的方法(return (a <= 0.0D) ? 0.0D - a : a;)。

参考:JDK 17 中的的实现:java/lang/Math.java

再进一步

有人提出了意见,认为目前官方的实现 too slow(6506405: Math.abs(float) is slow #4711)。

这就是 jdk-18+6 中引入的新方案(java/lang/Math.java#L1600~L1604):

public static double abs(double a) {
    return Double.longBitsToDouble(Double.doubleToRawLongBits(a) & DoubleConsts.MAG_BIT_MASK);
}

DoubleConsts.MAG_BIT_MASK 就是 0x7fffffffffffffffL, 0 + 63 个 1。

原理就是,通过位运算,清除符号位(使之为 0)。

参考资料与拓展阅读

#43 GitHub 上的一些备选方案

2021-09-13

内容管理系统 / CMS

cms stars:>10000 在 GitHub 上搜索,再经过一些筛选,剩下以下项目:

  1. hugo shields.io, 静态网站生成
  2. wagtail shields.io, Django
  3. grav shields.io
  4. django-cms shields.io
  5. netlify-cms, NodeJS, MIT 静态网站生成,Git-based
  6. Keystone shields.io, NodeJS
  7. Strapi shields.io, NodeJS, headless CMS
  8. Ghost shields.io, NodeJS
  9. October shields.io, PHP, Laravel

论坛 / BBS / Forum

forum stars:>2000 在 GitHub 上搜索,再经过一些筛选,剩下以下项目:

比较知名的项目:NodeBB,Flarum,phpBB,

  1. answerdev/answer Go shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    An open-source knowledge-based community software. You can use it quickly to build Q&A community for your products, customers, teams, and more.
  2. NodeBB/NodeBB Node.js shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    Node.js based forum software built for the modern web
  3. flarum/flarum PHP Symfony shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    Simple forum software for building great communities.
  4. phpbb/phpbb PHP shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    phpBB Development: phpBB is a popular open-source bulletin board written in PHP. This repository also contains the history of version 2.
  5. flaskbb/flaskbb Python Flask shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w
    A classic Forum Software in Python using Flask.
  6. rafalp/Misago Python Django DRF shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    Misago is fully featured modern forum application that is fast, scalable and responsive.
  7. discourse/discourse Ruby On Rails shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    A platform for community discussion. Free, open, simple.

SNS / MicroBlogging

  1. rocboss/paopao-ce Go shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    🔥A artistic "twitter like" community built on gin+zinc+vue+ts 清新文艺微社区
  2. humhub PHP shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w AGPL 3.0
    HumHub is an Open Source Enterprise Social Network. Easy to install, intuitive to use and extendable with countless freely available modules.

框架 / Web Framwork / RESTful API (Python)

  • Django shields.io
  • django-rest-framework shields.io
  • Flask shields.io
  • Sanic shields.io
  • Falcon shields.io
  • FastAPI shields.io
  • APIStar shields.io
  • Pyramid shields.io
  • Tornado shields.io
  • Twisted shields.io
  • daphne shields.io - A HTTP, HTTP2 and WebSocket protocol server for ASGI and ASGI-HTTP.
  • uvicorn shields.io - A lightning-fast ASGI server implementation, using uvloop and httptools.
  • bjoern shields.io - Asynchronous, very fast and written in C.
  • gunicorn shields.io - Pre-forked, ported from Ruby's Unicorn project.
  • uWSGI shields.io - A project aims at developing a full stack for building hosting services, written in C.
  • waitress shields.io - Multi-threaded, powers Pyramid.
  • werkzeug shields.io - A WSGI utility library for Python that powers Flask and can easily be embedded into your own projects.
  • uvloop shields.io - Ultra fast asyncio event loop.

数据校验 / Data Validation

  • Cerberus shields.io - A lightweight and extensible data validation library.
  • colander shields.io - Validating and deserializing data obtained via XML, JSON, an HTML form post.
  • jsonschema shields.io - An implementation of JSON Schema for Python.
  • schema shields.io - A library for validating Python data structures.
  • Schematics shields.io - Data Structure Validation.
  • valideer shields.io - Lightweight extensible data validation and adaptation library.
  • voluptuous shields.io - A Python data validation library.

#41 UMIDIGI 与 GPL 协议

2021-09-01

最近发生的一件事情挺有意思的,在得到 “上门自取” 的回复之后,有位美女替外国程序员依照 GPL 协议上门向国内的一家厂商索要内核代码。

深圳公司 UMIDIGI 生产安卓手机(联发科 Mediatek),面向海外用户。

波兰开发者 Patrycja (@ptrcnull) 想将移植 postmarketOS (基于 Alphine Linux) 到 UMIDIGI 的一款设备上,结果发现缺少 ft8719_dsi_fhdplus 屏幕驱动。

Patrycja 尝试联系 UMIDIGI,得到了以下回复:

Twitter

就是说,如果你要的话,你可以上门来取。我们可以认为这是想让 Patrycja 知难而退。

可是 Patrycja 八月 17 号在 Twitter 上抱怨之后,深圳科技博主 Naomi Wu 机械妖姬(@RealSexyCyborg,混 Youtube)主动提供帮忙,表示愿意代替 Patrycja 上门索取源代码。

然后机械妖姬 08/20 就拿着自拍杆勇闯 UMIDIGI 公司。
UMIDIGI 行政人员表示需要之前发邮件的 BEN 已经离职(我觉得很可能就坐在旁边看美女),然后提供源代码的事情需要先向老板请示,之后在相对友好的氛围下,机械妖姬离开了。

机械妖姬前往 UMIDIGI 公司

随后:

  • 08/25 UMIDIGI 向机械妖姬提供了相关文件。
  • 08/26 Patrcja 完成了系统移植,并向机械妖姬和 UMIDIGI 表示感谢。

这件事件我的评价是:

UMIDIGI 之前的邮件回复十分愚蠢,直接向法务部门咨询之后,通过合法的途径(可能涉及联发科的知识产权)将代码提供给他不就好了吗?
不过,如果不是事情闹大了,UMIDIGI 会不会提供源代码呢?这就不知道了,我们也不能以恶意揣度之。只能就事论事,在这次事件中,各方的表现都非常好。

#40 vCard (.vcf)

2021-09-01

VCF 是 Virtual Contact File 的简写,虚拟通讯录文件
PS:.vcf 后缀还有一个含义是 Variant Call Format,用在基因生物学方面。

#38 广告拦截器太过分了

2021-07-23

《科技爱好者周刊》第 167 期中,阮一峰十分愤慨的说广告拦截器太过分了。

因为他接到读者反馈,有篇文章中间的两段上下语义似乎不能接上,是否有写内容遗漏。然后检查之后发现是被广告拦截器拦截了。

阮一峰从使用非常广泛的规则集 EasyList (AdBlock 默认开启订阅,每 4 小时更新一次) 发现了很多针对他的规则,并从中摘抄了几句特别狠的:

! 拦截脚本 checker.js
ruanyifeng.com/blog/checker.js

! 隐藏指向 t.cn 的链接
ruanyifeng.com##a[href^="http://t.cn/"]

! 隐藏含有"培训"的段落
ruanyifeng.com##p:-abp-contains(培训)

# 曾经出现过,禁用所有 JS 代码:
ruanyifeng.com^$csp=script-src 'none'

我才知道,广告拦截可以做到这么精准的匹配,真心服!
阮一峰检测到用户开启广告拦截之后,就会不显示所有内容,取而代之的是这个提示:

您使用了广告拦截器,导致本站内容无法显示。
请将 www.ruanyifeng.com 加入白名单,解除广告屏蔽后,刷新页面。谢谢。

其他:

  1. 其实除了浏览器的广告拦截插件之外,HTTP 网关/代理、路由器也可以通过预设规则拦截广告。
  2. 我用过的广告拦截插件:AdBlock (getadblock.com),AdBlockPlus (adblockplus.org),uBlockOrigin (gorhill/uBlock)
  3. AdBlock (下载量 296278) 和 AdBlockPlus (下载量 174432) 的渊源:最早是有一个 Adblock 项目,由于 Adblock 停止更新,一位开发者启动了 AdblockPlus 项目,再后来又有公司基于 AdblockPlus 项目创建 AdBlock。

  4. uBlock (下载量 1658) 和 uBlock Origin (下载量 24666) 的渊源:uBlock 的创始人将项目转移给别人维护之后,好像对后来接收捐款的分配不满,自己又开了一个分支 uBlock Origin。

#37 思考:八进制的应用场景

2021-07-21

常见的进制:

  • 二进制, Binary /ˈbaɪnəri/, bin /bɪn/
    除了苏联设计过的一种计算机系统采用了平衡三进制(-1, 0, 1), 所有计算机系统都是采用的二进制, 二进制计算是程序员的一种必备技能, 其重要性不言而喻。
    常见的数字 16(四位), 256(八位), 1024(十位)等。
  • 八进制, Octal /ˈɒktl/, oct /ɒkt/
  • 十进制, Decimal /ˈdesɪm(ə)l/, dec /dek/
    十进制普遍认为是基于人类手指数量来设计的, 其深深的影响了我们的计算方式, 已经作为人类基本的数学认知。
  • 十六进制, Hexadecimal /ˌheksəˈdesɪml/, hex /heks/
    二进制计算机系统中, 一个字节定义为八位, 那么通常的选择是采用两个十六进制数来表示, 在记忆成本和便捷性方面达到一个最好的平衡。
    CPU 位数、地址总线宽度等, 通常是 4 的倍数, 比如:16 位的 8086 / 8088 有 20 位地址总线, 32 位的 386 / 486 / 奔腾 有 32 位地址总线, 64 位酷睿系列有 64 位地址总线。

那么,八进制用来干嘛?

刚才在维基百科上找到了答案:

Octal became widely used in computing when systems such as the UNIVAC 1050, PDP-8, ICL 1900 and IBM mainframes employed 6-bit, 12-bit, 24-bit or 36-bit words.

就是说早期大量机器采用了 6 位,12 位,24 位,36 位的实现,都是 3 的倍数,所以取八进制(3 位二进制数一组)来表示比较通用。

#36 GitHub Copilot 争议

2021-07-14

七月二号发了一篇《吊炸天的 GitHub Copilot》,我表示非常期待这种技术的到来。
但是我并不知道他们是怎么弄的,没有考虑到其 AI 采用的训练集可能涉及的版权问题。
可以看到最近针对 Copilot 产生了巨大的争议,当前开发者社区的这种申讨氛围可能会让 GitHub 放弃 Copilot。

首先,GitHub 承认 Copilot 采用公开仓库代码做训练,不论其授权协议是 GPL 还是啥。
这里面有巨大的版权风险,虽然 GitHub 官方声称不会直接复制粘贴代码,但这种可能看起来就是 “洗代码” 的行为,无法说服别人他们拥有新代码的支配权。
更何况有人拿出了一些证据来证明 Copilot 会直接 Ctrl C + Ctrl V。

最近我使用 vscode 的时候,可以看到有时它会给我一些提示,真的感觉很棒。我不想 Copilot 被抛弃,希望 GitHub 或者 Google、IBM、阿里,或别的公司或组织,能解决所有争议,提供类似的产品,更好的服务开发者。

#35 吊炸天的 GitHub Copilot

2021-07-02

一个月前看到了微软通过 OpenAI 独家授权的 GPT-3 弄了一个低代码编程语言 Power Fx,可以直接通过简单的自然语言来完成数据的各种复杂操作。
我看到演示,当时就非常惊讶。不过当时我觉得,估计也就只能到这个地步了(弄弄 Excel 公式),想要真的生成可用代码,可能还得等几年。
今天有看到了 GitHub 搞的这个 Copilot,我是真的惊了。
看网上的演示,就根据一点点注释,可以生成各种语言的代码,还可以提供多种选择。
Your AI pair programmer,真是此言不虚!
除非是从 GitHub 已有仓库中拿出来的,然后人工标注其用途(可能性非常小),否则这真是逆天了,超出了我对现阶段人工智能水平的认知。
不过现在是技术预览阶段,我已提交申请,不过不知道什么时候可以通过,我是真的想试试,要是用上这等神器,必定可以省不少事。
话说回来,老码农的价值肯定是会被压缩了一些些。
首先,还是得看看 Copilot 的水平,再说。
我现在真是太激动了,虽然可能会让程序员变得更卷,但是看到这样的技术进步,我还是非常开心。

Update @ 2021-11-01

Copilot 已经支持 neovim 和 JetBrains IDE (IDEA, PyCharm, WebStorm, PphStorm, Goland...) 了。

Update @2022-06-23

GitHub Copilot 宣布免费到 08/22,今后要收费,每个月 8 美元。我已经非常习惯 Copilot 了,但是这个价格对我来说还是不可接受的。

我搜索了一下 VSCode 的拓展商店,看到有一个新的 GitHub Copilot Nightly 版本,估计是给免费用户使用的。
另外还发现一个 GitHub Copilot Labs 拓展,可能是更加完善的版本,应该也是收费。

Update @2022-06-27