2018年11月22日木曜日

CREATING BLACK HOLES: DIVISION BY ZERO IN PRACTICE 7 Comments by: Sven Gregori

CREATING BLACK HOLES: DIVISION BY ZERO IN PRACTICE

 7 Comments November 21, 2018
Dividing by zero — the fundamental no-can-do of arithmetic. It is somewhat surrounded by mystery, and is a constant source for internet humor, whether it involves exploding microcontrollers, the collapse of the universe, or crashing your own world by having Siri tell you that you have no friends.
It’s also one of the few things gcc will warn you about by default, which caused a rather vivid discussion with interesting insights when I recently wrote about compiler warnings. And if you’re running a modern operating system, it might even send you a signal that something’s gone wrong and let you handle it in your code. Dividing by zero is more than theoretical, and serves as a great introduction to signals, so let’s have a closer look at it.
Chances are, the first time you heard about division itself back in elementary school, it was taught that dividing by zero is strictly forbidden — and obviously you didn’t want your teacher call the cops on you, so you obeyed and refrained from it. But as with many other things in life, the older you get, the less restrictive they become, and dividing by zero eventually turned from forbidden into simply being impossible and yielding an undefined result.
And indeed, if a = b/0, it would mean in reverse that a×0 = b. If b itself was zero, the equation would be true for every single number there is, making it impossible to define a concrete value for a. And if b was any other value, no single value multiplied by zero could result in anything non-zero. Once we move into the realms of calculus, we will learn that infinity appears to be the answer, but that’s in the end just replacing one abstract, mind-boggling concept with another one. And it won’t answer one question: how does all this play out in a processor?

FLOATING POINT DIVISION BY ZERO

Let’s start with floating point numbers. Nowadays, they are usually represented and stored in memory using the IEEE 754 format, splitting the value itself into separate fields for the sign, exponent, and fraction. If we look at a float or double in memory, it won’t make too much sense, as its actual value is constructed from those three separate fields. To demonstrate that, we can cast a float into an int and print it. Note that we have to do so by pointer conversion, otherwise we’d just end up with the integer part of the floating point number.
1
2
3
4
float fval = 12.34f;
int *iptr  = (int *) &fval;
printf("%f -> 0x%08x\n", fval, *iptr);
// output: 12.340000 -> 0x414570a4
Neither 0x414570a4 nor 1095069860 would give us any hint that this is the number 12.34. However, this form of representation leaves room for some special cases like infinity and not a number, something dividing zero by zero will result in, since that equation has no single answer and therefore cannot be represented by a regular number. That means we can just go ahead and divide by zero all we want, the IEEE 754 format has us covered.
1
2
3
4
5
6
7
fval = 1 / 0.0f;
printf("%f -> 0x%08x\n", fval, *iptr);
// output: inf -> 0x7f800000

fval = 0 / 0.0f;
printf("%f -> 0x%08x\n", fval, *iptr);
// output: -nan -> 0xffc00000
In other words, floating point numbers have built-in mechanisms to deal with division by zero without wreaking havoc. Not that it really helps, we cannot do much with either inf or nan, and arithmetic operations on infinity either remain infinity (maybe with changed signedness), or turn into nan, which is a dead end. No arithmetic operation can turn nan into anything else ever again.

INTEGER DIVISION BY ZERO

If you tried out the previous example for yourself, you may have noticed that after all the talk about compiler warnings that led us here in the first place, you didn’t see a single one of them. Since floating point numbers have their own, well-defined way to handle division by zero, it doesn’t pose a threat that the compiler would have to warn about. This is all great, provided we have an FPU in place, or enough resources to emulate floating point operations in software. However, if we’re working with integers, we have one major problem: the concept of infinity doesn’t exist in the limited range of values we have available. Even if we ended up with a data type with an enormous amount of bits, best we could represent is a really, really large number, but never infinity. This time, the compiler will warn about an obvious attempt to divide by zero.
1
2
3
4
5
// zerodiv.c
int main(void) {
    int i = 10/0;
    return 0;
}
1
2
3
4
5
6
$ gcc -o zerodiv zerodiv.c
zerodiv.c: In function ‘main’:
zerodiv.c:3:15: warning: division by zero [-Wdiv-by-zero]
     int i = 10/0;
               ^
$
So what’s going to happen if we still do it? After all, it’s just a warning, we got a functional executable from the compiler, and nothing is going to stop us running it. Well, let’s do it and see what happens on x86_64:
1
2
3
$ ./zerodiv
Floating point exception (core dumped)
$
There we go, since we cannot represent the result in any way, the program simply crashed. But what caused that crash?
In more complex processors, the instruction set offers dedicated division opcodes, and the division itself is performed in hardware. This allows the processor to detect a zero divisor before the operation itself is executed, causing a hardware exception. In our case, the operating system caught the exception and raised the floating point exception signal SIGFPE — don’t mind the somewhat misleading name of it. So just like with floating point numbers, the hardware division instruction has means in place to avoid dealing with actually dividing by zero. But what about processors without such dedicated hardware, like an 8-bit AVR or ARM Cortex-M0? After all, division isn’t a particularly new concept that was only made possible by modern processor technology.
If you think back to your school days, pen-and-paper division was mainly a combination of shifts, comparison, and subtraction in a loop. These are all basic instructions available in even the simplest processors, which allows them to replace division with a series of other instructions. AN0964 describes the concept for AVR if you’re curious. While there are also more sophisticated approaches, in some cases the division instruction isn’t any different behind the scenes, it just has dedicated hardware for it to wrap it in a single opcode.
However, for the processor, there is no telling that a particular series of regular operations are actually performing a division that require to keep an eye on the divisor not being zero. We’d have to manually check that ourselves before the division. If we don’t, it just keeps happily on looping and comparing for all eternity (or 1267+ years) until it finds a number that fulfills the impossible, i.e. it simply gets stuck in an infinite loop. Mechanical calculators are great devices to demonstrate this.
While this will leave the universe intact, it essentially renders your program unresponsive just like any other endless loop, which is obviously bad since it’s most likely supposed to handle other things. If you’re therefore performing division or modulo operations on an architecture without a hardware divider, and the divisor isn’t from a predefined set that guarantees it won’t be zero, make sure you check the value beforehand. Well, in fact, it’s probably a good idea even with the right hardware support. An instant crash might be better than a possibly undetected endless loop, but either way, your code won’t be doing what it’s supposed to do.

BACK TO THAT SIGFPE

One difference with receiving a hardware exception that either turns into an interrupt or a signal like SIGFPE, is that we can act on it. Not to go too deep into the details: signals are a way to notify our running program that some certain event has happened — segmentation fault (SIGSEGV), program termination (SIGTERM and the more drastic SIGKILL), or the previously encountered floating point exception SIGFPE, to name a few. We can catch most of these signals in our code and act upon them however needed. For example, if we catch SIGINT, we can shut down gracefully when CTRL+C is pressed, or simply ignore it.
This means, we could catch a division by zero occurrence and for example save our current program state, or write some log file entries that lets us reproduce how we got here in the first place, which might help us avoiding the situation in the future. Well, time to write a signal handler then. Note that the code is slightly simplified, the sigaction man page is a good source for more information about masks and error handling.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// zerodiv.c
#include <stdio.h>
#include <signal.h>

// SIGFPE callback function
void sigfpe_handler(int sig, siginfo_t *si, void *arg) {
    // print some info and see if it was division by zero that got us here
    printf("SIGFPE received at %p due to %s\n", si->si_addr,
            ((si->si_code == FPE_INTDIV) ? "div by zero""other reasons"));
    // do whatever should be done
}

int main(void) {
    int i = 123;
    struct sigaction sa;

    // set sigfpe_handler() as our handler function
    sa.sa_sigaction = sigfpe_handler;
    // make sure we get the siginfo_t content and reset the original handler
    sa.sa_flags = SA_SIGINFO | SA_RESETHAND;

    // set up the signal handling for SIGFPE
    sigaction(SIGFPE, &sa, NULL);

    printf("before: %d\n", i);
    i /= 0; // doing the nasty
    printf("after:  %d\n", i);

    return 0;
}
If we compile it and run it, we can expect the division by zero warning, and then get an output like this:
1
2
3
4
5
$ ./zerodiv
before: 123
SIGFPE received at 0x55f2c333b208 due to div by zero
Floating point exception (core dumped)
$
Since we added the SA_RESETHAND flag, the program gets still terminated with the original exception. If we omit the flag, it won’t, but that doesn’t mean we would have successfully worked around the problem. On x86_64, the signal handler simply ends up in an endless loop, printing the message over and over again. We’d have to explicitly terminate the process by calling exit(int) in the signal handler.
On a side note, it appears that the ARM Cortex-A53 processor (the one you find on a Raspberry Pi) automatically resets the exception flag once handled, and therefore the program continues after returning from the signal handler, displaying after: 0. This suggests that the division by zero is defined to result in zero. I did not succeed resetting the flag on x86_64, hence the endless loop, but that doesn’t necessarily mean it’s not possible, I simply wasn’t able to achieve it myself. However, there’s one other thing we can do on x86_64: skip the division.

Skipping The Division

Note the third void *arg parameter in the signal handler callback? It will contain the context of the moment the signal was received, which gives us access to the saved registers, including the instruction pointer, which will be restored when we leave the signal handler. If we disassemble our code, we will see that our division in this particular example is a 2-byte instruction:
1
2
3
4
5
6
7
$ objdump -d ./zerodiv |grep -2 div
    125b:       b9 00 00 00 00          mov    $0x0,%ecx
    1260:       99                      cltd  
    1261:   --> f7 f9                   idiv   %ecx
    1263:       89 85 5c ff ff ff       mov    %eax,-0xa4(%rbp)
    1269:       8b 85 5c ff ff ff       mov    -0xa4(%rbp),%eax
$
If we increased the instruction pointer register RIP (or EIP on 32-bit x86) by two bytes, the execution would continue with the mov instruction after the idiv when we return from the signal handler. This kind of register tweaking sounds like a really bad idea, so you probably shouldn’t be doing something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#define __USE_GNU // this and the file order is important to succeed
#include <ucontext.h>
#include <signal.h>

// adjusted signal handler
void sigfpe_handler(int sig, siginfo_t *si, void *arg) {
    // cast arg to context struct pointer holding the registers
    ucontext_t *ctx = arg;
    // print some info
    printf("SIGFPE received at %p\n", si->si_addr);
    // add 2 bytes to the instruction pointer register
    ctx->uc_mcontext.gregs[REG_RIP] += 2;
}

// main() remained as it was
1
2
3
4
5
$ ./zerodiv
before: 123
SIGFPE received at 0x555c1ef5b243
after:  123
$
Tadaa — the division was skipped and the program survived. The value itself remained the same as before the attempted division. Obviously, any subsequent operation relying on the division’s result will most likely be useless and/or has unknown consequences. Same case with the Raspberry Pi earlier that yielded zero as result. Just because a random outcome was defined for an otherwise undefined situation doesn’t mean the underlying math was solved and everything makes suddenly sense. Failing fast and determined is often times the better option.
So there we have it. You won’t create black holes when dividing by zero, but you won’t get much useful out of it either.
 
ゼロ除算の発見は日本です:
∞???    
∞は定まった数ではない////
人工知能はゼロ除算ができるでしょうか:

とても興味深く読みました:2014年2月2日 4周年を超えました:
ゼロ除算の発見と重要性を指摘した:日本、再生核研究所


ゼロ除算関係論文・本

ダ・ヴィンチの名言 格言|無こそ最も素晴らしい存在
                     

ゼロ除算の発見はどうでしょうか:
Black holes are where God divided by zero:

再生核研究所声明371(2017.6.27)ゼロ除算の講演― 国際会議 
https://ameblo.jp/syoshinoris/entry-12287338180.html

1/0=0、0/0=0、z/0=0
http://ameblo.jp/syoshinoris/entry-12276045402.html
1/0=0、0/0=0、z/0=0
http://ameblo.jp/syoshinoris/entry-12263708422.html
1/0=0、0/0=0、z/0=0
http://ameblo.jp/syoshinoris/entry-12272721615.html
Division By Zero(ゼロ除算)1/0=0、0/0=0、z/0=0
ゼロ除算(ゼロじょざん、division by zero)1/0=0、0/0=0、z/0=0

ソクラテス・プラトン・アリストテレス その他
https://ameblo.jp/syoshinoris/entry-12328488611.html

ドキュメンタリー 2017: 神の数式 第2回 宇宙はなぜ生まれたのか
https://www.youtube.com/watch?v=iQld9cnDli4
〔NHKスペシャル〕神の数式 完全版 第3回 宇宙はなぜ始まったのか
https://www.youtube.com/watch?v=DvyAB8yTSjs&t=3318s
〔NHKスペシャル〕神の数式 完全版 第1回 この世は何からできているのか
https://www.youtube.com/watch?v=KjvFdzhn7Dc
NHKスペシャル 神の数式 完全版 第4回 異次元宇宙は存在するか
https://www.youtube.com/watch?v=fWVv9puoTSs

再生核研究所声明 411(2018.02.02):  ゼロ除算発見4周年を迎えて
https://ameblo.jp/syoshinoris/entry-12348847166.html

再生核研究所声明 416(2018.2.20):  ゼロ除算をやってどういう意味が有りますか。何か意味が有りますか。何になるのですか - 回答
再生核研究所声明 417(2018.2.23):  ゼロ除算って何ですか - 中学生、高校生向き 回答
再生核研究所声明 418(2018.2.24):  割り算とは何ですか? ゼロ除算って何ですか - 小学生、中学生向き 回答
再生核研究所声明 420(2018.3.2): ゼロ除算は正しいですか,合っていますか、信用できますか - 回答

2018.3.18.午前中 最後の講演: 日本数学会 東大駒場、函数方程式論分科会 講演書画カメラ用 原稿
The Japanese Mathematical Society, Annual Meeting at the University of Tokyo. 2018.3.18.
https://ameblo.jp/syoshinoris/entry-12361744016.html より
918日(火) 14:1015:00
和算とゼロ除算
齋藤三郎・奥村 
京都大学数理解析研究所 111 号室
(講演精神・要旨)

https://note.mu/ysaitoh/n/n1d38a681644f


再生核研究所声明 424(2018.3.29):  レオナルド・ダ・ヴィンチとゼロ除算
再生核研究所声明 427(2018.5.8): 神の数式、神の意志 そしてゼロ除算

Title page of Leonhard Euler, Vollständige Anleitung zur Algebra, Vol. 1 (edition of 1771, first published in 1770), and p. 34 from Article 83, where Euler explains why a number divided by zero gives infinity.
私は数学を信じない。 アルバート・アインシュタイン / I don't believe in mathematics. Albert Einstein→ゼロ除算ができなかったからではないでしょうか。
1423793753.460.341866474681。                            

Einstein's Only Mistake: Division by Zero
神の数式:
神の数式が解析関数でかけて居れば、 特異点でローラン展開して、正則部の第1項を取れば、 何時でも有限値を得るので、 形式的に無限が出ても 実は問題なく 意味を有します。
物理学者如何でしょうか。

 https://plaza.jp.rakuten-static.com/img/user/diary/new.gif
カテゴリ:カテゴリ未分類
​そこで、計算機は何時、1/0=0 ができるようになるでしょうか。 楽しみにしています。 もうできる進化した 計算機をお持ちの方は おられないですね。
これは凄い、面白い事件では? 計算機が人間を超えている 例では?

面白いことを発見しました。 計算機は 正しい答え 0/0=0
を出したのに、 この方は 間違いだと 言っている、思っているようです。
0/0=0 は 1300年も前に 算術の発見者によって与えられたにも関わらず、世界史は間違いだと とんでもないことを言ってきた。 世界史の恥。 実は a/0=0 が 何時も成り立っていた。 しかし、ここで 分数の意味を きちんと定義する必要がある。 計算機は、その意味さえ知っているようですね。 計算機、人間より賢くなっている 様が 出て居て 実に 面白い。
https://steemkr.com/utopian-io/@faisalamin/bug-zero-divide-by-zero-answers-is-zero
2018.10.11.11:23
カテゴリ:カテゴリ未分類
面白いことを発見しました。 計算機は 正しい答え 0/0=0
を出したのに、 この方は 間違いだと 言っている、思っているようです。
0/0=0 は 1300年も前に 算術の発見者によって与えられたにも関わらず、世界史は間違いだと とんでもないことを言ってきた。 実は a/0=0 が 何時も成り立っていた。しかし、ここで 分数の意味を きちんと定義する必要がある。 計算機は、その意味さえ知っているようですね。 計算機、人間より賢くなっている様が 出て居て 実に面白い。

 
https://steemkr.com/utopian-io/@faisalamin/bug-zero-divide-by-zero-answers-is-zero
2018.10.11.11:23

ゼロ除算、ゼロで割る問題、分からない、正しいのかなど、 良く理解できない人が 未だに 多いようです。そこで、簡潔な一般的な 解説を思い付きました。 もちろん、学会などでも述べていますが、 予断で 良く聞けないようです。まず、分数、a/b は a  割る b のことで、これは 方程式 x=a の解のことです。ところが、 b がゼロならば、 どんな xでも 0 x =0 ですから、a がゼロでなければ、解は存在せず、 従って 100/0 など、ゼロ除算は考えられない、できないとなってしまいます。 普通の意味では ゼロ除算は 不可能であるという、世界の常識、定説です。できない、不可能であると言われれば、いろいろ考えたくなるのが、人間らしい創造の精神です。 基本方程式 b x=a が b がゼロならば解けない、解が存在しないので、困るのですが、このようなとき、従来の結果が成り立つような意味で、解が考えられないかと、数学者は良く考えて来ました。 何と、 そのような方程式は 何時でも唯一つに 一般化された意味で解をもつと考える 方法があります。 Moore-Penrose 一般化逆の考え方です。 どんな行列の 逆行列を唯一つに定める 一般的な 素晴らしい、自然な考えです。その考えだと、 b がゼロの時、解はゼロが出るので、 a/0=0 と定義するのは 当然です。 すなわち、この意味で 方程式の解を考えて 分数を考えれば、ゼロ除算は ゼロとして定まる ということです。ただ一つに定まるのですから、 この考えは 自然で、その意味を知りたいと 考えるのは、当然ではないでしょうか?初等数学全般に影響を与える ユークリッド以来の新世界が 現れてきます。
ゼロ除算の誤解は深刻:

最近、3つの事が在りました。

私の簡単な講演、相当な数学者が信じられないような誤解をして、全然理解できなく、目が回っているいるような印象を受けたこと、
相当ゼロ除算の研究をされている方が、基本を誤解されていたこと、1/0 の定義を誤解されていた。
相当な才能の持ち主が、連続性や順序に拘って、4年以上もゼロ除算の研究を避けていたこと。

これらのことは、人間如何に予断と偏見にハマった存在であるかを教えている。
まずは ゼロ除算は不可能であるの 思いが強すぎで、初めからダメ、考えない、無視の気持ちが、強い。 ゼロ除算を従来の 掛け算の逆と考えると、不可能であるが 証明されてしまうので、割り算の意味を拡張しないと、考えられない。それで、 1/0,0/0,z/0 などの意味を発見する必要がある。 それらの意味は、普通の意味ではないことの 初めの考えを飛ばして ダメ、ダメの感情が 突っ走ている。 非ユークリッド幾何学の出現や天動説が地動説に変わった世界史の事件のような 形相と言える。
2018.9.22.6:41
ゼロ除算の4つの誤解:
1.      ゼロでは割れない、ゼロ除算は 不可能である との考え方に拘って、思考停止している。 普通、不可能であるは、考え方や意味を拡張して 可能にできないかと考えるのが 数学の伝統であるが、それができない。
2.      可能にする考え方が 紹介されても ゼロ除算の意味を誤解して、繰り返し間違えている。可能にする理論を 素直に理解しない、 強い従来の考えに縛られている。拘っている。
3.      ゼロ除算を関数に適用すると 強力な不連続性を示すが、連続性のアリストテレス以来の 連続性の考えに囚われていて 強力な不連続性を受け入れられない。数学では、不連続性の概念を明確に持っているのに、不連続性の凄い現象に、ゼロ除算の場合には 理解できない。
4.      深刻な誤解は、ゼロ除算は本質的に定義であり、仮定に基づいているので 疑いの気持ちがぬぐえず、ダメ、怪しいと誤解している。数学が公理系に基づいた理論体系のように、ゼロ除算は 新しい仮定に基づいていること。 定義に基づいていることの認識が良く理解できず、誤解している。
George Gamow (1904-1968) Russian-born American nuclear physicist and cosmologist remarked that "it is well known to students of high school algebra" that division by zero is not valid; and Einstein admitted it as {\bf the biggest blunder of his life} [1]:1. Gamow, G., My World Line (Viking, New York). p 44, 1970.

Eπi =-1 (1748)(Leonhard Euler)
1/0=0/0=0 (2014年2月2日再生核研究所)


1+1=2  (      )
a2+b2=c2 (Pythagoras)
1/0=0/0=0(2014年2月2日再生核研究所)
​​​​​​​

0 件のコメント:

コメントを投稿