<!DOCTYPE html>

<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="mobile-web-app-capable" content="yes">
    <title>
        Atomic floating-point min/max semantics
    </title>

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/3.5.0/octicons.min.css" integrity="sha256-QiWfLIsCT02Sdwkogf6YMiQlj4NE84MKkzEMkZnMGdg=" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css" integrity="sha256-vtR0hSWRc3Tb26iuN2oZHt3KRUomwTufNIf5/4oeCyg=" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@hackmd/emojify.js@2.1.0/dist/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" />
    <style>
        @import url(https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,500,500i|Source+Code+Pro:300,400,500|Source+Sans+Pro:300,300i,400,400i,600,600i|Source+Serif+Pro&subset=latin-ext);.hljs{background:#fff;color:#333;display:block;overflow-x:auto;padding:.5em}.hljs-comment,.hljs-meta{color:#969896}.hljs-emphasis,.hljs-quote,.hljs-string,.hljs-strong,.hljs-template-variable,.hljs-variable{color:#df5000}.hljs-keyword,.hljs-selector-tag,.hljs-type{color:#a71d5d}.hljs-attribute,.hljs-bullet,.hljs-literal,.hljs-number,.hljs-symbol{color:#0086b3}.hljs-built_in,.hljs-builtin-name{color:#005cc5}.hljs-name,.hljs-section{color:#63a35c}.hljs-tag{color:#333}.hljs-attr,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-title{color:#795da3}.hljs-addition{background-color:#eaffea;color:#55a532}.hljs-deletion{background-color:#ffecec;color:#bd2c00}.hljs-link{text-decoration:underline}.markdown-body{word-wrap:break-word;font-size:16px;line-height:1.5}.markdown-body:after,.markdown-body:before{content:"";display:table}.markdown-body:after{clear:both}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:#c00}.markdown-body .anchor{float:left;line-height:1;margin-left:-20px;padding-right:4px}.markdown-body .anchor:focus{outline:none}.markdown-body blockquote,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-bottom:16px;margin-top:0}.markdown-body hr{background-color:#e7e7e7;border:0;height:.25em;margin:24px 0;padding:0}.markdown-body blockquote{border-left:.25em solid #ddd;color:#777;font-size:16px;padding:0 1em}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body kbd,.popover kbd{background-color:#fcfcfc;border:1px solid;border-color:#ccc #ccc #bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb;color:#555;display:inline-block;font-size:11px;line-height:10px;padding:3px 5px;vertical-align:middle}.markdown-body .loweralpha{list-style-type:lower-alpha}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{font-weight:600;line-height:1.25;margin-bottom:16px;margin-top:24px}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#000;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 code,.markdown-body h1 tt,.markdown-body h2 code,.markdown-body h2 tt,.markdown-body h3 code,.markdown-body h3 tt,.markdown-body h4 code,.markdown-body h4 tt,.markdown-body h5 code,.markdown-body h5 tt,.markdown-body h6 code,.markdown-body h6 tt{font-size:inherit}.markdown-body h1{font-size:2em}.markdown-body h1,.markdown-body h2{border-bottom:1px solid #eee;padding-bottom:.3em}.markdown-body h2{font-size:1.5em}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body h5{font-size:.875em}.markdown-body h6{color:#777;font-size:.85em}.markdown-body ol,.markdown-body ul{padding-left:2em}.markdown-body ol.no-list,.markdown-body ul.no-list{list-style-type:none;padding:0}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-bottom:0;margin-top:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{padding-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{font-size:1em;font-style:italic;font-weight:700;margin-top:16px;padding:0}.markdown-body dl dd{margin-bottom:16px;padding:0 16px}.markdown-body table{display:block;overflow:auto;width:100%;word-break:normal;word-break:keep-all}.markdown-body table th{font-weight:700}.markdown-body table td,.markdown-body table th{border:1px solid #ddd;padding:6px 13px}.markdown-body table tr{background-color:#fff;border-top:1px solid #ccc}.markdown-body table tr:nth-child(2n){background-color:#f8f8f8}.markdown-body img{background-color:#fff;box-sizing:initial;max-width:100%}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{background-color:initial;max-width:none;vertical-align:text-top}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{border:1px solid #ddd;display:block;float:left;margin:13px 0 0;overflow:hidden;padding:7px;width:auto}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{clear:both;color:#333;display:block;padding:5px 0 0}.markdown-body span.align-center{clear:both;display:block;overflow:hidden}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{clear:both;display:block;overflow:hidden}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{background-color:#0000000a;border-radius:3px;font-size:85%;margin:0;padding:.2em 0}.markdown-body code:after,.markdown-body code:before,.markdown-body tt:after,.markdown-body tt:before{content:"\00a0";letter-spacing:-.2em}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body pre{word-wrap:normal}.markdown-body pre>code{background:#0000;border:0;font-size:100%;margin:0;padding:0;white-space:pre;word-break:normal}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{border-radius:3px;font-size:85%;line-height:1.45;overflow:auto}.markdown-body:not(.next-editor) pre{background-color:#f7f7f7;padding:16px}.markdown-body pre code,.markdown-body pre tt{word-wrap:normal;background-color:initial;border:0;display:inline;line-height:inherit;margin:0;max-width:auto;overflow:visible;padding:0}.markdown-body pre code:after,.markdown-body pre code:before,.markdown-body pre tt:after,.markdown-body pre tt:before{content:normal}.markdown-body .csv-data td,.markdown-body .csv-data th{font-size:12px;line-height:1;overflow:hidden;padding:5px;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-line-num{background:#fff;border:0;padding:10px 8px 9px;text-align:right}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{background:#f8f8f8;border-top:0;font-weight:700}.news .alert .markdown-body blockquote{border:0;padding:0 0 0 40px}.activity-tab .news .alert .commits,.activity-tab .news .markdown-body blockquote{padding-left:0}.task-list-item{list-style-type:none}.task-list-item label{font-weight:400}.task-list-item.enabled label{cursor:pointer}.task-list-item+.task-list-item{margin-top:3px}.task-list-item-checkbox{cursor:default!important;float:left;margin:.31em 0 .2em -1.3em!important;vertical-align:middle}.markdown-body{max-width:758px;overflow:visible!important;padding-bottom:40px;padding-top:40px;position:relative}.markdown-body.next-editor{overflow-x:hidden!important}.markdown-body .emoji{vertical-align:top}.markdown-body pre{border:inherit!important}.markdown-body code{color:inherit!important}.markdown-body pre code .wrapper{display:-moz-inline-flex;display:-ms-inline-flex;display:-o-inline-flex;display:inline-flex}.markdown-body pre code .gutter{float:left;overflow:hidden;-webkit-user-select:none;user-select:none}.markdown-body pre code .gutter.linenumber{border-right:3px solid #6ce26c!important;box-sizing:initial;color:#afafaf!important;cursor:default;display:inline-block;min-width:20px;padding:0 8px 0 0;position:relative;text-align:right;z-index:4}.markdown-body pre code .gutter.linenumber>span:before{content:attr(data-linenumber)}.markdown-body pre code .code{float:left;margin:0 0 0 16px}.markdown-body .gist .line-numbers{border-bottom:none;border-left:none;border-top:none}.markdown-body .gist .line-data{border:none}.markdown-body .gist table{border-collapse:inherit!important;border-spacing:0}.markdown-body code[data-gist-id]{background:none;padding:0}.markdown-body code[data-gist-id]:after,.markdown-body code[data-gist-id]:before{content:""}.markdown-body code[data-gist-id] .blob-num{border:unset}.markdown-body code[data-gist-id] table{margin-bottom:unset;overflow:unset}.markdown-body code[data-gist-id] table tr{background:unset}.markdown-body[dir=rtl] pre{direction:ltr}.markdown-body[dir=rtl] code{direction:ltr;unicode-bidi:embed}.markdown-body .alert>p:last-child{margin-bottom:0}.markdown-body pre.abc,.markdown-body pre.flow-chart,.markdown-body pre.graphviz,.markdown-body pre.mermaid,.markdown-body pre.sequence-diagram,.markdown-body pre.vega{background-color:inherit;border-radius:0;overflow:visible;text-align:center;white-space:inherit}.markdown-body pre.abc>code,.markdown-body pre.flow-chart>code,.markdown-body pre.graphviz>code,.markdown-body pre.mermaid>code,.markdown-body pre.sequence-diagram>code,.markdown-body pre.vega>code{text-align:left}.markdown-body pre.abc>svg,.markdown-body pre.flow-chart>svg,.markdown-body pre.graphviz>svg,.markdown-body pre.mermaid>svg,.markdown-body pre.sequence-diagram>svg,.markdown-body pre.vega>svg{height:100%;max-width:100%}.markdown-body pre>code.wrap{word-wrap:break-word;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap}.markdown-body .alert>p:last-child,.markdown-body .alert>ul:last-child{margin-bottom:0}.markdown-body summary{display:list-item}.markdown-body summary:focus{outline:none}.markdown-body details summary{cursor:pointer}.markdown-body details:not([open])>:not(summary){display:none}.markdown-body figure{margin:1em 40px}.markdown-body .mark,.markdown-body mark{background-color:#fff1a7}.vimeo,.youtube{background-color:#000;background-position:50%;background-repeat:no-repeat;background-size:contain;cursor:pointer;display:table;overflow:hidden;text-align:center}.vimeo,.youtube{position:relative;width:100%}.youtube{padding-bottom:56.25%}.vimeo img{object-fit:contain;width:100%;z-index:0}.youtube img{object-fit:cover;z-index:0}.vimeo iframe,.youtube iframe,.youtube img{height:100%;left:0;position:absolute;top:0;width:100%}.vimeo iframe,.youtube iframe{vertical-align:middle;z-index:1}.vimeo .icon,.youtube .icon{color:#fff;height:auto;left:50%;opacity:.3;position:absolute;top:50%;transform:translate(-50%,-50%);transition:opacity .2s;width:auto;z-index:0}.vimeo:hover .icon,.youtube:hover .icon{opacity:.6;transition:opacity .2s}.slideshare .inner,.speakerdeck .inner{position:relative;width:100%}.slideshare .inner iframe,.speakerdeck .inner iframe{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%}.figma{display:table;padding-bottom:56.25%;position:relative;width:100%}.figma iframe{border:1px solid #eee;bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%}.markmap-container{height:300px}.markmap-container>svg{height:100%;width:100%}.MJX_Assistive_MathML{display:none}#MathJax_Message{z-index:1000!important}.ui-infobar{color:#777;margin:25px auto -25px;max-width:760px;position:relative;z-index:2}.toc .invisable-node{list-style-type:none}.ui-toc{bottom:20px;position:fixed;z-index:998}.ui-toc.both-mode{margin-left:8px}.ui-toc.both-mode .ui-toc-label{border-bottom-left-radius:0;border-top-left-radius:0;height:40px;padding:10px 4px}.ui-toc-label{background-color:#e6e6e6;border:none;color:#868686;transition:opacity .2s}.ui-toc .open .ui-toc-label{color:#fff;opacity:1;transition:opacity .2s}.ui-toc-label:focus{background-color:#ccc;color:#000;opacity:.3}.ui-toc-label:hover{background-color:#ccc;opacity:1;transition:opacity .2s}.ui-toc-dropdown{margin-bottom:20px;margin-top:20px;max-height:70vh;max-width:45vw;overflow:auto;padding-left:10px;padding-right:10px;text-align:inherit;width:25vw}.ui-toc-dropdown>.toc{max-height:calc(70vh - 100px);overflow:auto}.ui-toc-dropdown[dir=rtl] .nav{letter-spacing:.0029em;padding-right:0}.ui-toc-dropdown a{overflow:hidden;text-overflow:ellipsis;white-space:pre}.ui-toc-dropdown .nav>li>a{color:#767676;display:block;font-size:13px;font-weight:500;padding:4px 20px}.ui-toc-dropdown .nav>li:first-child:last-child>ul,.ui-toc-dropdown .toc.expand ul{display:block}.ui-toc-dropdown .nav>li>a:focus,.ui-toc-dropdown .nav>li>a:hover{background-color:initial;border-left:1px solid #000;color:#000;padding-left:19px;text-decoration:none}.ui-toc-dropdown[dir=rtl] .nav>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav>li>a:hover{border-left:none;border-right:1px solid #000;padding-right:19px}.ui-toc-dropdown .nav>.active:focus>a,.ui-toc-dropdown .nav>.active:hover>a,.ui-toc-dropdown .nav>.active>a{background-color:initial;border-left:2px solid #000;color:#000;font-weight:700;padding-left:18px}.ui-toc-dropdown[dir=rtl] .nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav>.active>a{border-left:none;border-right:2px solid #000;padding-right:18px}.ui-toc-dropdown .nav .nav{display:none;padding-bottom:10px}.ui-toc-dropdown .nav>.active>ul{display:block}.ui-toc-dropdown .nav .nav>li>a{font-size:12px;font-weight:400;padding-bottom:1px;padding-left:30px;padding-top:1px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>a{padding-right:30px}.ui-toc-dropdown .nav .nav>li>ul>li>a{font-size:12px;font-weight:400;padding-bottom:1px;padding-left:40px;padding-top:1px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a{padding-right:40px}.ui-toc-dropdown .nav .nav>li>a:focus,.ui-toc-dropdown .nav .nav>li>a:hover{padding-left:29px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav .nav>li>a:hover{padding-right:29px}.ui-toc-dropdown .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>a:hover{padding-left:39px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a:hover{padding-right:39px}.ui-toc-dropdown .nav .nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>a{font-weight:500;padding-left:28px}.ui-toc-dropdown[dir=rtl] .nav .nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>a{padding-right:28px}.ui-toc-dropdown .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>a{font-weight:500;padding-left:38px}.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active>a{padding-right:38px}.markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang^=ja] .markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,ＭＳ ゴシック,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang=zh-tw] .markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang=zh-cn] .markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html .markdown-body[lang^=ja]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,ＭＳ ゴシック,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html .markdown-body[lang=zh-tw]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html .markdown-body[lang=zh-cn]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang^=ja] .ui-toc-dropdown{font-family:Source Sans Pro,Helvetica,Arial,Meiryo UI,MS PGothic,ＭＳ Ｐゴシック,sans-serif}html[lang=zh-tw] .ui-toc-dropdown{font-family:Source Sans Pro,Helvetica,Arial,Microsoft JhengHei UI,微軟正黑UI,sans-serif}html[lang=zh-cn] .ui-toc-dropdown{font-family:Source Sans Pro,Helvetica,Arial,Microsoft YaHei UI,微软雅黑UI,sans-serif}html .ui-toc-dropdown[lang^=ja]{font-family:Source Sans Pro,Helvetica,Arial,Meiryo UI,MS PGothic,ＭＳ Ｐゴシック,sans-serif}html .ui-toc-dropdown[lang=zh-tw]{font-family:Source Sans Pro,Helvetica,Arial,Microsoft JhengHei UI,微軟正黑UI,sans-serif}html .ui-toc-dropdown[lang=zh-cn]{font-family:Source Sans Pro,Helvetica,Arial,Microsoft YaHei UI,微软雅黑UI,sans-serif}.ui-affix-toc{max-height:70vh;max-width:15vw;overflow:auto;position:fixed;top:0}.back-to-top,.expand-toggle,.go-to-bottom{color:#999;display:block;font-size:12px;font-weight:500;margin-left:10px;margin-top:10px;padding:4px 10px}.back-to-top:focus,.back-to-top:hover,.expand-toggle:focus,.expand-toggle:hover,.go-to-bottom:focus,.go-to-bottom:hover{color:#563d7c;text-decoration:none}.back-to-top,.go-to-bottom{margin-top:0}.ui-user-icon{background-position:50%;background-repeat:no-repeat;background-size:cover;border-radius:50%;display:block;height:20px;margin-bottom:2px;margin-right:5px;margin-top:2px;width:20px}.ui-user-icon.small{display:inline-block;height:18px;margin:0 0 .2em;vertical-align:middle;width:18px}.ui-infobar>small>span{line-height:22px}.ui-infobar>small .dropdown{display:inline-block}.ui-infobar>small .dropdown a:focus,.ui-infobar>small .dropdown a:hover{text-decoration:none}.ui-more-info{color:#888;cursor:pointer;vertical-align:middle}.ui-more-info .fa{font-size:16px}.ui-connectedGithub,.ui-published-note{color:#888}.ui-connectedGithub{line-height:23px;white-space:nowrap}.ui-connectedGithub a.file-path{color:#888;padding-left:22px;text-decoration:none}.ui-connectedGithub a.file-path:active,.ui-connectedGithub a.file-path:hover{color:#888;text-decoration:underline}.ui-connectedGithub .fa{font-size:20px}.ui-published-note .fa{font-size:20px;vertical-align:top}.unselectable{-webkit-user-select:none;-o-user-select:none;user-select:none}.selectable{-webkit-user-select:text;-o-user-select:text;user-select:text}.inline-spoiler-section{cursor:pointer}.inline-spoiler-section .spoiler-text{background-color:#333;border-radius:2px}.inline-spoiler-section .spoiler-text>*{opacity:0}.inline-spoiler-section .spoiler-img{filter:blur(10px)}.inline-spoiler-section.raw{background-color:#333;border-radius:2px}.inline-spoiler-section.raw>*{opacity:0}.inline-spoiler-section.unveil{cursor:auto}.inline-spoiler-section.unveil .spoiler-text{background-color:#3333331a}.inline-spoiler-section.unveil .spoiler-text>*{opacity:1}.inline-spoiler-section.unveil .spoiler-img{filter:none}@media print{blockquote,div,img,pre,table{page-break-inside:avoid!important}a[href]:after{font-size:12px!important}}.markdown-body.slides{color:#222;position:relative;z-index:1}.markdown-body.slides:before{background-color:currentColor;bottom:0;box-shadow:0 0 0 50vw;content:"";display:block;left:0;position:absolute;right:0;top:0;z-index:-1}.markdown-body.slides section[data-markdown]{background-color:#fff;margin-bottom:1.5em;position:relative;text-align:center}.markdown-body.slides section[data-markdown] code{text-align:left}.markdown-body.slides section[data-markdown]:before{content:"";display:block;padding-bottom:56.23%}.markdown-body.slides section[data-markdown]>div:first-child{left:1em;max-height:100%;overflow:hidden;position:absolute;right:1em;top:50%;transform:translateY(-50%)}.markdown-body.slides section[data-markdown]>ul{display:inline-block}.markdown-body.slides>section>section+section:after{border:3px solid #777;content:"";height:1.5em;position:absolute;right:1em;top:-1.5em}.site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,sans-serif}html[lang^=ja] .site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,ＭＳ ゴシック,sans-serif}html[lang=zh-tw] .site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif}html[lang=zh-cn] .site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif}body{font-smoothing:subpixel-antialiased!important;-webkit-font-smoothing:subpixel-antialiased!important;-moz-osx-font-smoothing:auto!important;-webkit-overflow-scrolling:touch;font-family:Source Sans Pro,Helvetica,Arial,sans-serif;letter-spacing:.025em}html[lang^=ja] body{font-family:Source Sans Pro,Helvetica,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,ＭＳ ゴシック,sans-serif}html[lang=zh-tw] body{font-family:Source Sans Pro,Helvetica,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif}html[lang=zh-cn] body{font-family:Source Sans Pro,Helvetica,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}abbr[data-original-title],abbr[title]{cursor:help}body.modal-open{overflow-y:auto;padding-right:0!important}svg{text-shadow:none}
    </style>
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    	<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js" integrity="sha256-3Jy/GbSLrg0o9y5Z5n1uw0qxZECH7C6OQpVBgNFYa0g=" crossorigin="anonymous"></script>
    	<script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js" integrity="sha256-g6iAfvZp+nDQ2TdTR/VVKJf3bGro4ub5fvWSWVRi2NE=" crossorigin="anonymous"></script>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js" integrity="sha256-8E4Is26QH0bD52WoQpcB+R/tcWQtpzlCojrybUd7Mxo=" crossorigin="anonymous"></script>
    <![endif]-->
</head>

<body>
    <div id="doc" class="markdown-body container-fluid comment-enabled comment-inner" data-hard-breaks="true"><h1 id="Atomic-floating-point-minmax" data-id="Atomic-floating-point-minmax"><a class="anchor hidden-xs" href="#Atomic-floating-point-minmax" title="Atomic-floating-point-minmax"><span class="octicon octicon-link"></span></a><span>Atomic floating-point min/max</span></h1><p><strong><span>Document number</span></strong><span>: P3008R0.</span><br>
<strong><span>Date</span></strong><span>: 2023-10-13.</span><br>
<strong><span>Authors</span></strong><span>: Gonzalo Brito Gadeschi, David Sankel &lt;</span><a href="mailto:dsankel@adobe.com" target="_blank" rel="noopener"><span>dsankel@adobe.com</span></a><span>&gt;.</span><br>
<strong><span>Reply to</span></strong><span>: gonzalob </span><em><span>at</span></em><span> </span><a href="http://nvidia.com" target="_blank" rel="noopener"><span>nvidia.com</span></a><span> .</span><br>
<strong><span>Audience</span></strong><span>: SG1, LEWG.</span></p><style>
ins {
    color:green;
    background-color:yellow;
    text-decoration:underline;
}
del {
    color:red;
    background-color:yellow;
    text-decoration:line-through;
}
bdi {
    color:black;
    background-color:lightblue;
    text-decoration:underline;
}
.markdown-body {
    max-width: 900px;
    text-align: justify;
}
</style><p><big><span>Table of Contents</span></big></p><p><span class="toc"><ul>
<li><a href="#Atomic-floating-point-minmax" title="Atomic floating-point min/max">Atomic floating-point min/max</a></li>
<li><a href="#Introduction" title="Introduction">Introduction</a></li>
<li><a href="#Survey-of-programming-language-floating-point-minmax-API-semantics" title="Survey of programming language floating-point min/max API semantics">Survey of programming language floating-point min/max API semantics</a><ul>
<li><a href="#C-stdminstdmax" title="C++ std::min/std::max">C++ std::min/std::max</a></li>
<li><a href="#C-fminfmax" title="C fmin/fmax">C fmin/fmax</a></li>
<li><a href="#C23-minimummaximumminimumNumbermaximumNumber" title="C23 minimum/maximum/minimumNumber/maximumNumber">C23 minimum/maximum/minimumNumber/maximumNumber</a></li>
</ul>
</li>
<li><a href="#Impact-of-replacing-min-with-fminimum_num" title="Impact of replacing min with fminimum_num">Impact of replacing min with fminimum_num</a></li>
<li><a href="#Survey-of-hardware-atomic-floating-point-minmax-API-semantics" title="Survey of hardware atomic floating-point min/max API semantics">Survey of hardware atomic floating-point min/max API semantics</a><ul>
<li><a href="#Performance-impact-of-atomicltfloating-pointgtfetch_min_max-semantics" title="Performance impact of atomic<floating-point>::fetch_min/_max semantics">Performance impact of atomic&lt;floating-point&gt;::fetch_min/_max semantics</a></li>
</ul>
</li>
<li><a href="#Design-space" title="Design space">Design space</a><ul>
<li><a href="#Alternatives-that-reserve-fetch_minfetch_max-for-stdminstdmax" title="Alternatives that reserve fetch_min/fetch_max for std::min/std::max">Alternatives that reserve fetch_min/fetch_max for std::min/std::max</a></li>
<li><a href="#Alternatives-that-provide-fetch_minfetch_max-with-different-semantics" title="Alternatives  that provide fetch_min/fetch_max with different semantics">Alternatives  that provide fetch_min/fetch_max with different semantics</a></li>
</ul>
</li>
<li><a href="#Wording" title="Wording">Wording</a></li>
<li><a href="#References" title="References">References</a></li>
</ul>
</span></p><h1 id="Introduction" data-id="Introduction"><a class="anchor hidden-xs" href="#Introduction" title="Introduction"><span class="octicon octicon-link"></span></a><span>Introduction</span></h1><p><a href="https://wg21.link/P0493R4" target="_blank" rel="noopener"><span>P0493R4</span></a><span> </span><em><span>Atomic minimum/maximum</span></em><span> proposes the addition of </span><code>atomic&lt;T&gt;::fetch_min</code><span> and </span><code>atomic&lt;T&gt;::::fetch_max</code><span> operations, which atomically perform </span><code>std::min</code><span> and </span><code>std::max</code><span> computations.</span></p><p><span>The Varna '23 plenary rejected these semantics for </span><code>atomic&lt;floating-point&gt;</code><span> types due to safety concerns. These semantics deviate from the current standard practice, as defined by </span><a href="https://ieeexplore.ieee.org/document/8766229" target="_blank" rel="noopener"><span>IEEE 754-2019</span></a><span> recommendations and the capabilities of available hardware.</span></p><p><span>This paper aims to empower the committee to ship these APIs in the C++ 26 timeframe. It reviews the semantics proposed in </span><a href="https://wg21.link/P0493R4" target="_blank" rel="noopener"><span>P0493R4</span></a><span> and standard practice: </span><a href="https://iremi.univ-reunion.fr/IMG/pdf/ieee-754-2008.pdf" target="_blank" rel="noopener"><span>IEEE 754-2008</span></a><span>, </span><a href="https://ieeexplore.ieee.org/document/8766229" target="_blank" rel="noopener"><span>IEEE 754-2019</span></a><span>, and available hardware support. It then explores the design space and evaluates alternative solutions.</span></p><p><span>Finally, the authors propose two coherent alternatives and concrete wording changes to align </span><a href="https://wg21.link/P0493R4" target="_blank" rel="noopener"><span>P0493R4</span></a><span> with some of the semantics </span><a href="https://ieeexplore.ieee.org/document/8766229" target="_blank" rel="noopener"><span>IEEE 754-2019</span></a><span> recommends all vendors implement.</span></p><h1 id="Survey-of-programming-language-floating-point-minmax-API-semantics" data-id="Survey-of-programming-language-floating-point-minmax-API-semantics"><a class="anchor hidden-xs" href="#Survey-of-programming-language-floating-point-minmax-API-semantics" title="Survey-of-programming-language-floating-point-minmax-API-semantics"><span class="octicon octicon-link"></span></a><span>Survey of programming language floating-point min/max API semantics</span></h1><p><span>This section provides an overview of widely used floating-point </span><code>min</code><span>/</span><code>max</code><span> semantics. We will focus on how various </span><code>min</code><span>/</span><code>max</code><span> semantics handle the following “corner cases”:</span></p><ul>
<li><strong><span>Signed zero</span></strong><span>: Should </span><code>-0</code><span> be considered less than </span><code>+0</code><span> (</span><code>-0 &lt; +0</code><span>), or should </span><code>-0</code><span> and </span><code>+0</code><span> be treated as equivalent (</span><code>-0 == +0</code><span>) such that, e.g., </span><code>min(+0, -0) = +0</code><span>?</span></li>
<li><strong><span>One quiet NaN</span></strong><span> (qNaN): when one of the arguments is a qNaN, should it be treated as </span><em><span>Missing Data</span></em><span> and the other argument returned, or should it be treated as an </span><em><span>Error</span></em><span> and propagated?</span>
<ul>
<li><strong><span>Missing Data</span></strong><span>: returns the other argument: </span><code>min(x, qNaN) = x</code><span> and </span><code>min(qNaN, x) = x</code><span>.</span></li>
<li><strong><span>Error</span></strong><span>: propagates the qNaN:</span><code>min(x, qNaN) = qNaN</code><span> and </span><code>min(qNaN, x) = qNaN</code><span>.</span></li>
</ul>
</li>
</ul><p><span>Explicitly and intentionally, this paper ignores:</span></p><ul>
<li><strong><span>Signaling NaNs</span></strong><span>: C specifies they are undefined, and C++ does not specify anything about them.</span></li>
<li><strong><span>NaN payload propagation</span></strong><span>: IEEE 754 does not mandate it, and programming languages do not interpret NaN payloads.</span></li>
</ul><p><strong><span>Table 1</span></strong><span> summarizes the semantics of the different C or C++ APIs regarding Signed Zeros and Quiet NaN handling and documents whether implementations that implement particular IEEE 754 semantics are valid implementations of the APIs.</span></p><table>
<thead>
<tr>
<th><span>C API</span></th>
<th><span>C++ API</span></th>
<th><span>Signed Zeros</span></th>
<th><span>One qNaN</span></th>
<th><span>IEEE 754 impl valid?</span></th>
</tr>
</thead>
<tbody>
<tr>
<td><span>-</span></td>
<td><code>min</code><br><code>max</code></td>
<td><span>Equivalent</span></td>
<td><span>Undefined</span><br><span>(Second[1])</span></td>
<td><span>Ternary [0]</span></td>
</tr>
<tr>
<td><code>fmin</code><br><code>fmax</code></td>
<td><span>-</span></td>
<td><span>Equivalent</span><br><span>or</span><br><code>-0 &lt; +0</code><span> (QoI) [2]</span></td>
<td><span>Missing Data</span></td>
<td><code>minNum</code><br><code>maxNum</code><br><span>or (QoI)</span><br><code>minimumNumber</code><br><code>maximumNumber</code></td>
</tr>
<tr>
<td><code>fminimum</code><br><code>fmaximum</code></td>
<td><span>-</span></td>
<td><code>-0 &lt; +0</code></td>
<td><span>Error</span></td>
<td><code>minimum</code><br><code>maximum</code></td>
</tr>
<tr>
<td><code>fminimum_num</code><br><code>fmaximum_num</code></td>
<td><span>-</span></td>
<td><code>-0 &lt; +0</code></td>
<td><span>Missing Data</span></td>
<td><code>minimumNumber</code><br><code>maximumNumber</code></td>
</tr>
</tbody>
</table><p><span>[0] Ternary semantics: </span><code>min(x,y) = y &lt; x? y : x</code><span>, </span><code>max(x,y) = x &lt; y? y : x</code><span>; both return the first argument if the arguments are equivalent.</span><br>
<span>[1] In practice, the same implementation as for valid values is used, and the second argument is returned, since in the ternary expression, the conditional involving a NaN is always false.</span><br>
<span>[2] QoI: “Quality of Implementation”, i.e., </span><code>fmin</code><span> does not require </span><code>-0 &lt; +0</code><span>, but it recommends that high quality implementations implement it.</span></p><h2 id="C-stdminstdmax" data-id="C-stdminstdmax"><a class="anchor hidden-xs" href="#C-stdminstdmax" title="C-stdminstdmax"><span class="octicon octicon-link"></span></a><span>C++ </span><code>std::min</code><span>/</span><code>std::max</code></h2><p><span>The </span><code>std::min</code><span> and </span><code>std::max</code><span> algorithms have a </span><em><span>precondition</span></em><span> on their arguments (</span><a href="http://eel.is/c++draft/alg.min.max#1" target="_blank" rel="noopener"><span>[alg.min.max.1]</span></a><span>):</span></p><blockquote>
<p><span>Preconditions: For the first form, </span><code>T</code><span> meets the </span><a href="http://eel.is/c++draft/utility.arg.requirements#tab:cpp17.lessthancomparable" target="_blank" rel="noopener"><span>Cpp17LessThanComparable</span></a><span> requirements (Table </span><a href="http://eel.is/c++draft/utility.arg.requirements#tab:cpp17.lessthancomparable" target="_blank" rel="noopener"><span>Cpp17LessThanComparable</span></a><span>).</span></p>
</blockquote><p><span>which NaNs do not satisfy.</span></p><details><summary>Rationale.</summary>
<br>
<p><span>Per </span><a href="https://wg21.link/structure.requirements#4" target="_blank" rel="noopener"><span>[structure.requirements#4]</span></a><span> and </span><a href="https://wg21.link/structure.requirements#8" target="_blank" rel="noopener"><span>[structure.requirements#8]</span></a><span>, which uses floating points as an example, the syntactic requirements apply to the type, but the semantic requirements only apply to the values actually passed to the algorithm.</span></p>
<p><span>The </span><a href="http://eel.is/c++draft/utility.arg.requirements#tab:cpp17.lessthancomparable" target="_blank" rel="noopener"><span>Cpp17LessThanComparable</span></a><span> concept, which requires:</span></p>
<blockquote>
<p><code>&lt;</code><span> is a strict weak ordering relation (</span><a href="http://eel.is/c++draft/alg.sorting#general-4" target="_blank" rel="noopener"><span>[alg.sorting]</span></a><span>)</span></p>
</blockquote>
<p><span>Which is specified in </span><a href="http://eel.is/c++draft/alg.sorting#general-4" target="_blank" rel="noopener"><span>[alg.sorting#general-4]</span></a><span>:</span></p>
<blockquote>
<p><span>The term strict refers to the requirement of an irreflexive relation (</span><code>!comp(x, x)</code><span> for all </span><code>x</code><span>), and the term weak to requirements that are not as strong as those for a total ordering, but stronger than those for a partial ordering. If we define </span><code>equiv(a, b)</code><span> as </span><code>!comp(a, b) &amp;&amp; !comp(b, a)</code><span>, then the requirements are that </span><code>comp</code><span> and </span><code>equiv</code><span> both be transitive relations:</span></p>
<ol>
<li><code>comp(a, b) &amp;&amp; comp(b, c)</code><span> implies </span><code>comp(a, c)</code></li>
<li><code>equiv(a, b)</code><span> &amp;&amp; </span><code>equiv(b, c)</code><span> implies </span><code>equiv(a, c)</code></li>
</ol>
<p><span>[Note 1: Under these conditions, it can be shown that</span><br>
<span>3. </span><code>equiv</code><span> is an equivalence relation,</span><br>
<span>4. </span><code>comp</code><span> induces a well-defined relation on the equivalence classes determined by </span><code>equiv</code><span>, and</span><br>
<span>5. the induced relation is a strict total ordering.</span><br>
<span>— end note]</span></p>
</blockquote>
<p><span>and is satisfied by all floating-point values with the exception of NaNs.</span></p>
</details><br><p><span>For valid values, implementations follow (</span><a href="http://eel.is/c++draft/alg.min.max" target="_blank" rel="noopener"><span>[alg.min.max]</span></a><span>):</span></p><blockquote>
<p><span>[</span><code>std::min</code><span>]:</span><br>
<em><span>Returns</span></em><span>: The smaller value. Returns the first argument when the arguments are equivalent.</span></p>
</blockquote><blockquote>
<p><span>[</span><code>std::max</code><span>]:</span><br>
<em><span>Returns</span></em><span>: The larger value. Returns the first argument when the arguments are equivalent.</span></p>
</blockquote><p><span>which preserves equivalence between </span><code>-0</code><span> and </span><code>+0</code><span> (</span><a href="https://cpp.godbolt.org/z/84j4Kse46" target="_blank" rel="noopener"><span>godbolt</span></a><span>):</span></p><pre><code class="cpp hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span></div><div class="code"><span class="token comment">// Listing 1: std::min/std::max</span>
<span class="token keyword">auto</span> min <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token keyword">auto</span><span class="token operator">&amp;</span> a<span class="token punctuation">,</span> <span class="token keyword">auto</span><span class="token operator">&amp;</span> b<span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token keyword">auto</span><span class="token operator">&amp;</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> std<span class="token double-colon punctuation">::</span><span class="token function">min</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// y &lt; x ? y : x;</span>
<span class="token keyword">auto</span> max <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token keyword">auto</span><span class="token operator">&amp;</span> a<span class="token punctuation">,</span> <span class="token keyword">auto</span><span class="token operator">&amp;</span> b<span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token keyword">auto</span><span class="token operator">&amp;</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> std<span class="token double-colon punctuation">::</span><span class="token function">max</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// x &lt; y ? y : x;</span>

<span class="token function">min</span><span class="token punctuation">(</span>qNaN<span class="token punctuation">,</span> <span class="token number">2.f</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// UB: qNaN</span>
<span class="token function">max</span><span class="token punctuation">(</span>qNaN<span class="token punctuation">,</span> <span class="token number">2.f</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// UB: qNaN</span>
<span class="token function">min</span><span class="token punctuation">(</span><span class="token number">2.f</span><span class="token punctuation">,</span> qNaN<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// UB: 2</span>
<span class="token function">max</span><span class="token punctuation">(</span><span class="token number">2.f</span><span class="token punctuation">,</span> qNaN<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// UB: 2</span>
<span class="token function">min</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// -0</span>
<span class="token function">max</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// -0</span>
<span class="token function">min</span><span class="token punctuation">(</span><span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// +0</span>
<span class="token function">max</span><span class="token punctuation">(</span><span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// +0</span>
</div></div></code></pre><p data-original-title="" title=""><span>In all standard library implementations surveyed, the manifestation of the </span><em><span>undefined behavior</span></em><span> is to return the first argument; this is depicted in the example with “UB: qNaN” and “UB: 2”.</span></p><p><span>The behavior of these semantics are:</span></p><ul>
<li><strong><span>Signed Zero</span></strong><span>: equivalent.</span></li>
<li><strong><span>One qNaN</span></strong><span>: undefined.</span></li>
</ul><p><span>In concurrent programs, these semantics become even less i</span><span class="ui-comment-inline-span">ntuitive, e.g., the sign of the result of a concurrent computation may depend on the order in which an observer sitting on top of the memory location observes the operations from different threads. If negative and positive zero are equivalent, the sign of this final result may differ across executions.</span></p><h2 id="C-fminfmax" data-id="C-fminfmax"><a class="anchor hidden-xs" href="#C-fminfmax" title="C-fminfmax"><span class="octicon octicon-link"></span></a><span>C </span><code>fmin</code><span>/</span><code>fmax</code></h2><p><span>The semantics of the </span><a href="https://open-std.org/JTC1/SC22/WG14/www/docs/n3096.pdf" target="_blank" rel="noopener"><span>ISO/IEC 9899:2024 (C23) standard</span></a><span> </span><code>fmin</code><span>/</span><code>fmax</code><span> functions are compatible with implementations of </span><a href="https://iremi.univ-reunion.fr/IMG/pdf/ieee-754-2008.pdf" target="_blank" rel="noopener"><span>IEEE 754-2008</span></a><span> </span><code>minNum</code><span> and </span><code>maxNum</code><span> and of </span><a href="https://ieeexplore.ieee.org/document/8766229" target="_blank" rel="noopener"><span>IEEE 754-2019</span></a><span> </span><code>minimumNumber</code><span>/</span><code>maximumNumber</code><span>. The </span><code>fmin</code><span>/</span><code>fmax</code><span> functions are available in C++ through the </span><code>&lt;cmath&gt;</code><span> header as </span><code>std::fmin/std::fmax</code><span>.</span></p><p><span>The semantics of C’s </span><code>fmin</code><span>/</span><code>fmax</code></p><ul>
<li><strong><span>Signed zero</span></strong><span>:</span>
<ul>
<li><code>-0</code><span> may be equivalent to </span><code>+0</code><span> (</span><code>minNum</code><span>/</span><code>maxNum</code><span>)</span></li>
<li><code>-0 &lt; +0</code><span> allowed as QoI (</span><code>minimumNumber</code><span>/</span><code>maximumNumber</code><span> guarantee it).</span></li>
</ul>
</li>
<li><strong><span>One qNaN</span></strong><span>: missing data (other value returned).</span></li>
</ul><details><summary>(collapsible) C23 fmin/fmax specification.</summary>
<blockquote>
<p><span>[From C23 7.12.12 Maximum, minimum, and positive difference functions]</span></p>
<p><span>[fmax 7.12.12.2]: The </span><code>fmax</code><span> functions determine the maximum numeric value of their arguments. [Note 299]</span></p>
<p><span>[fmin 7.12.12.3]: The </span><code>fmin</code><span> functions determine the minimum numeric value of their arguments. [Note 300]</span></p>
<p><span>[Note 299]: Quiet NaN arguments are treated as missing data: if one argument is a quiet NaN and the other numeric, then the fmax functions choose the numeric value. See F.10.9.2.</span></p>
<p><span>[Note 300]: The </span><code>fmin</code><span> functions are analogous to the </span><code>fmax</code><span> functions in their treatment of quiet NaNs.</span></p>
<p><span>[Note 461]: Ideally, </span><code>fmax</code><span> would be sensitive to the sign of zero, for example </span><code>fmax(−0.0, +0.0)</code><span> would return </span><code>+0</code><span>; however, implementation in software might be impractical.</span></p>
<p><strong><span>NOTE 1</span></strong><span>: The </span><code>fmax</code><span> and </span><code>fmin</code><span> functions are similar to the fmaximum_num and fminimum_num functions, though may differ in which signed zero is returned when the arguments are differently signed zeros and in their treatment of signaling NaNs (see F.10.9.5).</span></p>
<p><span>[F.10.9.2 The fmax functions]: If just one argument is a NaN, the fmax functions return the other argument (if both arguments are NaNs, the functions return a NaN).</span></p>
<p><span>[F.10.9.3 The fmin functions]: The fmin functions are analogous to the fmax functions (see F.10.9.2).</span></p>
<p><span>[F.2.1]: This specification does not define the behavior of signaling NaNs.</span></p>
</blockquote>
</details><details><summary>(collapsible) [IEEE 754-2008] minNum/maxNum specification.</summary>
<blockquote>
<p><span>[5.3.1  General operations]</span><br>
<code>sourceFormat minNum(source, source)</code><br>
<code>sourceFormat maxNum(source, source)</code></p>
<p><code>minNum(x, y)</code><span> is the canonicalized number </span><code>x</code><span> if </span><code>x&lt;y</code><span>, </span><code>y</code><span> if </span><code>y&lt;x</code><span>, the canonicalized number if one operand is a number and the other a quiet NaN. Otherwise it is either </span><code>x</code><span> or </span><code>y</code><span>, canonicalized (this means results might differ among implementations).</span></p>
<p><code>maxNum(x, y)</code><span> is the canonicalized number </span><code>y</code><span> if </span><code>x&lt;y</code><span>, </span><code>x</code><span> if </span><code>y&lt;x</code><span>, the canonicalized number if one operand is a number and the other a quiet NaN. Otherwise it is either </span><code>x</code><span> or </span><code>y</code><span>, canonicalized (this means results might differ among implementations).</span></p>
</blockquote>
</details><br><p><span>That is, in the presence of a qNaN, these return the value, but signed zeros may or may not be equivalent:</span></p><pre><code class="cpp hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span></div><div class="code"><span class="token comment">// Listing 2: fmin/fmax</span>
<span class="token function">fmin</span><span class="token punctuation">(</span>qNaN<span class="token punctuation">,</span> <span class="token number">2.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token number">2</span>
<span class="token function">fmax</span><span class="token punctuation">(</span>qNaN<span class="token punctuation">,</span> <span class="token number">2.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token number">2</span>
<span class="token function">fmin</span><span class="token punctuation">(</span><span class="token number">2.f</span><span class="token punctuation">,</span> qNaN<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token number">2</span>
<span class="token function">fmax</span><span class="token punctuation">(</span><span class="token number">2.f</span><span class="token punctuation">,</span> qNaN<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token number">2</span>
<span class="token function">fmin</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">0</span> <span class="token operator">or</span> <span class="token operator">+</span><span class="token number">0</span>
<span class="token function">fmax</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">0</span> <span class="token operator">or</span> <span class="token operator">+</span><span class="token number">0</span>
<span class="token function">fmin</span><span class="token punctuation">(</span><span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">0</span> <span class="token operator">or</span> <span class="token operator">+</span><span class="token number">0</span>
<span class="token function">fmax</span><span class="token punctuation">(</span><span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">0</span> <span class="token operator">or</span> <span class="token operator">+</span><span class="token number">0</span>
</div></div></code></pre><p><a href="https://ieeexplore.ieee.org/document/8766229" target="_blank" rel="noopener"><span>IEEE 754-2019</span></a><span> removed </span><code>minNum</code><span> and</span><code>maxNum</code><span> operations and replaced them with </span><code>minimumNumber</code><span>/</span><code>maximumNumber</code><span> and </span><code>minimum</code><span>/</span><code>maximum</code><span>. These are surveyed in the next section. The gist of the rationale for their removal from </span><a href="https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf" target="_blank" rel="noopener"><span>The Removal/Demotion of </span><code>MinNum</code><span> and </span><code>MaxNum</code><span> Operations from IEEE 754-2018</span></a><span> is:</span></p><blockquote>
<p><span>These [minNum, maxNum] operations are removed from or demoted in IEEE std 754-2018, due to their non-associativity. […] With this non-associativity, different compilations or runs on parallel processing can return different answers […].</span></p>
</blockquote><h2 id="C23-minimummaximumminimumNumbermaximumNumber" data-id="C23-minimummaximumminimumNumbermaximumNumber"><a class="anchor hidden-xs" href="#C23-minimummaximumminimumNumbermaximumNumber" title="C23-minimummaximumminimumNumbermaximumNumber"><span class="octicon octicon-link"></span></a><span>C23 </span><code>minimum</code><span>/</span><code>maximum</code><span>/</span><code>minimumNumber</code><span>/</span><code>maximumNumber</code></h2><p><a href="https://ieeexplore.ieee.org/document/8766229" target="_blank" rel="noopener"><span>IEEE 754-2019</span></a><span> removed </span><code>minNum</code><span>/</span><code>maxNum</code><span> and recommends programming languages to provide their replacements instead: </span><code>minimumNumber</code><span>/</span><code>maximumNumber</code><span>/</span><code>minimum</code><span>/</span><code>maximum</code><span>.</span></p><details><summary>(collapsible) IEEE 754-2019 minimum/maximum/minimumNumber/maximumNumber specification.</summary>
<blockquote>
<p><span>[9.6 Minimum and maximum operations]: Language standards should define the following homogeneous general-computational operations for all</span><br>
<span>supported arithmetic formats:</span></p>
<pre><code>sourceFormat minimum(source, source)
sourceFormat minimumNumber(source, source)
sourceFormat maximum(source, source)
sourceFormat maximumNumber(source, source)
</code></pre>
<p><code>minimum(x, y)</code><span> is </span><code>x</code><span> if </span><code>x &lt; y</code><span>, </span><code>y</code><span> if </span><code>y &lt; x</code><span>, and a quiet NaN if either operand is a NaN, according to 6.2. For this operation, </span><code>−0</code><span> compares less than </span><code>+0</code><span>. Otherwise (i.e., when </span><code>x=y</code><span> and signs are the same) it is either </span><code>x</code><span> or </span><code>y</code><span>.</span></p>
<p><code>minimumNumber(x, y)</code><span> is </span><code>x</code><span> if </span><code>x&lt;y</code><span>, </span><code>y</code><span> if </span><code>y&lt;x</code><span>, and the number if one operand is a number and the other is a NaN. For this operation, </span><code>−0</code><span> compares less than </span><code>+0</code><span>. If </span><code>x = y</code><span> and signs are the same it is either </span><code>x</code><span> or </span><code>y</code><span>. If both operands are NaNs, a quiet NaN is returned, according to 6.2.</span></p>
<p><code>maximum(x, y)</code><span> is </span><code>x</code><span> if </span><code>x &gt; y</code><span>, </span><code>y</code><span> if </span><code>y &gt; x</code><span>, and a quiet NaN if either operand is a NaN, according to 6.2. For this operation, +0 compares greater than </span><code>−0</code><span>. Otherwise (i.e., when </span><code>x=y</code><span> and signs are the same) it is either </span><code>x</code><span> or </span><code>y</code><span>.</span></p>
<p><code>maximumNumber(x, y)</code><span> is </span><code>x</code><span> if </span><code>x&gt;y</code><span>, </span><code>y</code><span> if </span><code>y&gt;x</code><span>, and the number if one operand is a number and the other is a NaN. For this operation, </span><code>+0</code><span> compares greater than </span><code>−0</code><span>. If </span><code>x = y</code><span> and signs are the same it is either </span><code>x</code><span> or </span><code>y</code><span>. If both operands are NaNs, a quiet NaN is returned, according to 6.2.</span></p>
<p><span>[6.2 Operations with NaNs]: For an operation with quiet NaN inputs, except as stated otherwise, if a floating-point result is to be delivered the result shall be a canonical quiet NaN.</span></p>
</blockquote>
</details><details><summary>(collapsible) C23 fmaximum/fminimum/fmaximum_num/fminimum_num specification.</summary>
<blockquote>
<p><span>[From C23 7.12.12 Maximum, minimum, and positive difference functions]</span></p>
<p><span>[fmaximum - 7.12.12.4]: The </span><code>fmaximum</code><span> functions return the maximum value of their arguments.</span></p>
<p><span>The </span><code>fmaximum</code><span> functions determine the maximum value of their arguments. For these functions, </span><code>+0</code><span> is considered greater than </span><code>−0</code><span>. These functions differ from the </span><code>fmaximum_num</code><span> functions only in their treatment of NaN arguments (see F.10.9.4, F.10.9.5).</span></p>
<p><span>[fminimum - 7.12.12.5]: The </span><code>fminimum</code><span> functions return the minimum value of their arguments.</span></p>
<p><span>The </span><code>fminimum</code><span> functions determine the minimum value of their arguments. For these functions, </span><code>−0</code><span> is considered less than </span><code>+0</code><span>. These functions differ from the fminimum_num functions only in their treatment of NaN arguments (see F.10.9.4, F.10.9.5).</span></p>
<p><span>[fmaximum_num - 7.12.12.8]: The </span><code>fmaximum_num</code><span> functions return the maximum value of their numeric arguments.</span></p>
<p><span>The </span><code>fmaximum_num</code><span> functions determine the maximum value of their numeric arguments. They determine the number if one argument is a number and the other is a NaN. These functions differ from the </span><code>fmaximum</code><span> functions only in their treatment of NaN arguments (see F.10.9.4, F.10.9.5).</span></p>
<p><span>[fminimum_num - 7.12.12.9]: The </span><code>fminimum_num</code><span> functions return the minimum value of their numeric arguments.</span></p>
<p><span>The </span><code>fminimum_num</code><span> functions determine the minimum value of their numeric arguments. They determine the number if one argument is a number and the other is a NaN. These functions differ from the fminimum functions only in their treatment of NaN arguments (see F.10.9.4, F.10.9.5).</span></p>
<p><span>[F.10.9.4]: These functions treat NaNs like other functions in &lt;math.h&gt; (see F.10).</span></p>
<p><span>[F.10 Mathematics &lt;math.h&gt; and &lt;tgmath.h&gt;]: Functions with a NaN argument return a NaN result and raise no floating-point exception, except</span><br>
<span>where explicitly stated otherwise.</span></p>
</blockquote>
</details><br><p><span>The semantics of these functions matches for signed zero: </span><code>-0 &lt; +0</code><span>, and differs in their treatment when one argument is a qNaN:</span></p><ul>
<li><strong><span>Missing Data</span></strong><span>: </span><code>fminimumNumber</code><span>/</span><code>fmaximumNumber</code><span>, return the Number.</span></li>
<li><strong><span>Errors</span></strong><span>: </span><code>fminimum</code><span>/</span><code>fmaximum</code><span>, propagate the qNaN.</span></li>
</ul><pre><code class="cpp hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span></div><div class="code"><span class="token comment">// Listing 3: fminimum/fmaximum</span>
<span class="token function">fminimum</span><span class="token punctuation">(</span>qNaN<span class="token punctuation">,</span> <span class="token number">2.f</span><span class="token punctuation">)</span><span class="token operator">:</span> qNaN
<span class="token function">fmaximum</span><span class="token punctuation">(</span>qNaN<span class="token punctuation">,</span> <span class="token number">2.f</span><span class="token punctuation">)</span><span class="token operator">:</span> qNaN
<span class="token function">fminimum</span><span class="token punctuation">(</span><span class="token number">2.f</span><span class="token punctuation">,</span> qNaN<span class="token punctuation">)</span><span class="token operator">:</span> qNaN
<span class="token function">fmaximum</span><span class="token punctuation">(</span><span class="token number">2.f</span><span class="token punctuation">,</span> qNaN<span class="token punctuation">)</span><span class="token operator">:</span> qNaN
<span class="token function">fminimum</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">0</span>
<span class="token function">fmaximum</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">+</span><span class="token number">0</span>
<span class="token function">fminimum</span><span class="token punctuation">(</span><span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">0</span>
<span class="token function">fmaximum</span><span class="token punctuation">(</span><span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">+</span><span class="token number">0</span>
    
<span class="token function">fminimum_num</span><span class="token punctuation">(</span>qNaN<span class="token punctuation">,</span> <span class="token number">2.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token number">2</span>
<span class="token function">fmaximum_num</span><span class="token punctuation">(</span>qNaN<span class="token punctuation">,</span> <span class="token number">2.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token number">2</span>
<span class="token function">fminimum_num</span><span class="token punctuation">(</span><span class="token number">2.f</span><span class="token punctuation">,</span> qNaN<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token number">2</span>
<span class="token function">fmaximum_num</span><span class="token punctuation">(</span><span class="token number">2.f</span><span class="token punctuation">,</span> qNaN<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token number">2</span>
<span class="token function">fminimum_num</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">0</span>
<span class="token function">fmaximum_num</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">+</span><span class="token number">0</span>
<span class="token function">fminimum_num</span><span class="token punctuation">(</span><span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">0</span>
<span class="token function">fmaximum_num</span><span class="token punctuation">(</span><span class="token operator">+</span><span class="token number">0.f</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">0.f</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token operator">+</span><span class="token number">0</span>
</div></div></code></pre><h1 id="Impact-of-replacing-min-with-fminimum_num" data-id="Impact-of-replacing-min-with-fminimum_num"><a class="anchor hidden-xs" href="#Impact-of-replacing-min-with-fminimum_num" title="Impact-of-replacing-min-with-fminimum_num"><span class="octicon octicon-link"></span></a><span>Impact of replacing </span><code>min</code><span> with </span><code>fminimum_num</code></h1><p><strong><span>Table 2</span></strong><span> captures the impact of replacing </span><code>min</code><span> with </span><code>fminimum_num</code><span> is as follows:</span></p><table>
<thead>
<tr>
<th><code>std::min</code><span>/</span><code>std::max</code></th>
<th><code>fminimum_num</code><span>/</span><code>fmaximum_num</code></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>min(qNaN, 2.f); // UB: qNaN</code><br><code>max(qNaN, 2.f); // UB: qNaN</code><br><code>min(+0.f, -0.f); // +0</code><br><code>max(-0.f, +0.f); // -0</code></td>
<td><code>fminimum_num(qNaN, 2.f); // 2</code><br><code>fmaximum_num(qNaN, 2.f); // 2</code><br><code>fminimum_num(+0.f, -0.f); // -0</code><br><code>fmaximum_num(-0.f, +0.f); // +0</code></td>
</tr>
</tbody>
</table><p><span>That is, when the first input of </span><code>std::min</code><span>/</span><code>std::max</code><span> is a qNaN, then these switch from exhibiting </span><em><span>undefined behavior</span></em><span> to returning a number, and when signed zeros are involved, there is a case where the result has a different sign.</span></p><h1 id="Survey-of-hardware-atomic-floating-point-minmax-API-semantics" data-id="Survey-of-hardware-atomic-floating-point-minmax-API-semantics"><a class="anchor hidden-xs" href="#Survey-of-hardware-atomic-floating-point-minmax-API-semantics" title="Survey-of-hardware-atomic-floating-point-minmax-API-semantics"><span class="octicon octicon-link"></span></a><span>Survey of hardware atomic floating-point min/max API semantics</span></h1><p><span>On systems without native support for atomic floating-point min/max operations, these operations must be performed atomically, e.g., using a CAS loop, a compare-and-conditional-store loop, an LL/SC loop, or, e.g., by taking a lock, which would make the atomic not lock-free. In these systems, the memory latency overhead may outweight the cost of performing the actual </span><em><span>arithmetic</span></em><span> portion of the min/max operations, and extra effort may be required to ensure forward progress properties like starvation freedom, and therefore those systems are not considered here.</span></p><p><strong><span>Table 3</span></strong><span> surveys the publicly known Instruction Set Architectures (ISAs) with atomic floating-point min/max operations, which are all GPU ISAs:</span></p><table>
<thead>
<tr>
<th><span>Vendor</span></th>
<th><span>ISA</span></th>
<th><span>Instructions</span></th>
<th><span>IEEE-2019 compat</span></th>
<th><span>Signed Zero</span></th>
<th><span>Quiet NaN</span></th>
</tr>
</thead>
<tbody>
<tr>
<td><span>AMD</span></td>
<td><a href="https://www.amd.com/content/dam/amd/en/documents/instinct-tech-docs/instruction-set-architectures/instinct-mi200-cdna2-instruction-set-architecture.pdf" target="_blank" rel="noopener"><span>CDNA2</span></a><span>+</span></td>
<td><a href="https://www.amd.com/content/dam/amd/en/documents/instinct-tech-docs/instruction-set-architectures/instinct-mi200-cdna2-instruction-set-architecture.pdf" target="_blank" rel="noopener"><span>MIN</span></a><br><a href="https://www.amd.com/content/dam/amd/en/documents/instinct-tech-docs/instruction-set-architectures/instinct-mi200-cdna2-instruction-set-architecture.pdf" target="_blank" rel="noopener"><span>MAX</span></a></td>
<td><code>minimumNumber</code><br><code>maximumNumber</code></td>
<td><span>-0 &lt; +0</span></td>
<td><span>Missing Data</span></td>
</tr>
<tr>
<td><span>Intel</span></td>
<td><a href="https://www.intel.com/content/www/us/en/docs/graphics-for-linux/developer-reference/1-0/alchemist-arctic-sound-m.html" target="_blank" rel="noopener"><span>Xe ISA</span></a></td>
<td><code>AOP_FMIN</code><br><code>AOP_FMAX</code><span> [0]</span></td>
<td><code>minimumNumber</code><br><code>maximumNumber</code></td>
<td><span>-0 &lt; +0</span></td>
<td><span>Missing Data</span></td>
</tr>
<tr>
<td><span>NVIDIA</span></td>
<td><a href="https://docs.nvidia.com/cuda/parallel-thread-execution/index.html" target="_blank" rel="noopener"><span>PTX</span></a></td>
<td><a href="https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-atom" target="_blank" rel="noopener"><span>atom</span></a><br><a href="https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-red" target="_blank" rel="noopener"><span>red</span></a></td>
<td><code>minimumNumber</code><br><code>maximumNumber</code></td>
<td><span>-0 &lt; +0</span></td>
<td><span>Missing Data</span></td>
</tr>
<tr>
<td><span>Neutral [1]</span></td>
<td><a href="http://htmlpreview.github.io/?https://github.com/KhronosGroup/SPIRV-Registry/blob/main/extensions/EXT/SPV_EXT_shader_atomic_float_min_max.html" target="_blank" rel="noopener"><span>SPIR-V</span></a><span> Extension</span></td>
<td><a href="https://registry.khronos.org/SPIR-V/specs/unified1/OpenCL.ExtendedInstructionSet.100.html" target="_blank" rel="noopener"><span>OpAtomicFMinEXT</span></a><br><a href="https://registry.khronos.org/SPIR-V/specs/unified1/OpenCL.ExtendedInstructionSet.100.html" target="_blank" rel="noopener"><span>OpAtomicFMaxEXT</span></a></td>
<td><span>C </span><code>fmin</code><span>/</span><code>fmax</code></td>
<td><span>Equivalent;</span><br><span>QoI: -0 &lt; +0</span></td>
<td><span>Missing Data</span></td>
</tr>
</tbody>
</table><ul>
<li><span>[0]: Volume 2d: Command Reference: Structures, page 229.</span></li>
<li><span>[1]: The Vulkan extension corresponding to this SPIR-V extension is </span><code>VK_EXT_shader_atomic_float2</code><span>. Hardware that implements it is listed </span><a href="https://vulkan.gpuinfo.org/listdevicescoverage.php?extensionname=VK_EXT_shader_atomic_float2&amp;extensionfeature=shaderBufferFloat32AtomicMinMax&amp;platform=all" target="_blank" rel="noopener"><span>here</span></a><span> and </span><a href="https://vulkan.gpuinfo.org/listdevicescoverage.php?extensionname=VK_EXT_shader_atomic_float2&amp;extensionfeature=shaderSharedFloat32AtomicMinMax&amp;platform=all" target="_blank" rel="noopener"><span>here</span></a><span>.</span></li>
</ul><p><span>All the architectures surveyed order </span><code>-0 &lt; +0</code><span>, treat qNaNs as missing data, and implement IEEE 754-2019 </span><code>minimumNumber</code><span> and </span><code>maximumNumber</code><span> semantics. The SPIR-V extension requires C </span><code>fmin</code><span>/</span><code>fmax</code><span> semantics, which allows </span><code>-0 &lt; +0</code><span> but does not require it.</span></p><p><span>The semantics vendor implement are compatible with C23’s </span><code>fminimum_num</code><span>/</span><code>fmaximum_num</code><span>, and C’s </span><code>fmin</code><span>/</span><code>fmax</code><span>, but not with C++'s </span><code>std::min</code><span>/</span><code>std::max</code><span> due to the different outcomes when signed-zeros are involved.</span></p><h2 id="Performance-impact-of-atomicltfloating-pointgtfetch_min_max-semantics" data-id="Performance-impact-of-atomicltfloating-pointgtfetch_min_max-semantics"><a class="anchor hidden-xs" href="#Performance-impact-of-atomicltfloating-pointgtfetch_min_max-semantics" title="Performance-impact-of-atomicltfloating-pointgtfetch_min_max-semantics"><span class="octicon octicon-link"></span></a><span>Performance impact of </span><code>atomic&lt;floating-point&gt;::fetch_min</code><span>/</span><code>_max</code><span> semantics</span></h2><p><span>We compare the performance impact of two different </span><code>atomic&lt;floating-point&gt;::fetch_max</code><span> semantics:</span></p><ul>
<li><code>std::min</code><span>, which lowers to a CAS-loop that performs </span><code>std::min</code><span> (similar for compare-and-conditional-store), and</span></li>
<li><code>fminimum_num</code><span>, which lowers to native atomic operations,</span></li>
</ul><p><span>using a synthetic micro-benchmark that measures throughput in Giga Operations per Second (y-axis; logarithmic) as a function of the number of threads (x-axis; logarithmic) from 32 to 130’000 hardware threads on an </span><span class="ui-comment-inline-span">NVIDIA GPU system</span><span>.</span></p><img src="https://hackmd.io/_uploads/H1qPahSWp.png" class="center" width="400"><p><span>Modern concurrent systems provide dozens of millions of hardware threads operating on the same shared memory.</span></p><p><span>While native in-memory atomic operations increase throughput until its theoretical peak with just a few thousand threads, the performance of compare and swap strategies decreases as the number of threads increases due to excess contention. At just a few thousand threads, the performance is already four orders of magnitude worse (~10’000x) than that of native in-memory operations. This is why vendors of highly concurrent systems provide these operations.</span></p><p><span>While most CPU ISAs lack native hardware instructions for atomic floating-point min/max, their performance impact is expected to be similar to that of atomic integer min/max. Section 9 of </span><a href="https://wg21.link/P0493R4" target="_blank" rel="noopener"><span>P0493R4</span></a><span> presents benchmarks comparing the performance of a CAS-based implementation against an Arm v8.1 implementation using the native atomic </span><code>ldsmaxl</code><span> instruction. The benchmark covers a range of cores, from 2 to 64. The performance improvement of the native instruction ranges between 2.75x (2 cores) to 1.7x at 64 cores. We expect this behavior to transfer to atomic floating-point min/max.</span></p><p><span>We, unfortunately, do not have benchmarks for other hardware configurations at this time.</span></p><h1 id="Design-space" data-id="Design-space"><a class="anchor hidden-xs" href="#Design-space" title="Design-space"><span class="octicon octicon-link"></span></a><span>Design space</span></h1><p><span>The design space can be categoratized into two groups of alternatives:</span></p><ul>
<li><strong><span>Same semantics</span></strong><span>: </span><code>::fetch_min</code><span>/</span><code>_max</code><span> have </span><em><span>same</span></em><span> semantics as </span><code>std::min</code><span>/</span><code>std::max</code><span>,</span></li>
<li><strong><span>Different semantics</span></strong><span>: </span><code>::fetch_min</code><span>/</span><code>_max</code><span> have </span><em><span>different</span></em><span> semantics than </span><code>std::min</code><span>/</span><code>std::max</code><span>.</span></li>
</ul><p><span>The following table shows the subjectively most reasonable alternative of each category side-by-side, along with a summary of their advantages and disadvantages, which are analyzed in the following section.</span></p><p><strong><span>Table 4</span></strong><span>: Comparison of best alternative of each category.</span></p><table>
<thead>
<tr>
<th><span>Category</span></th>
<th><code>std::min</code><span> semantics</span></th>
<th><code>std::fminimum_num</code><span> semantics</span></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><span>Concrete Example</span></strong></td>
<td><code>x.fetch_min(y);</code><br><code>x.fetch_fminimum_num(y);</code></td>
<td><code>x.fetch_min(y, std::less{});</code><br><code>x.fetch_min(y);</code></td>
</tr>
<tr>
<td><strong><span>Pros</span></strong></td>
<td><span>- Parallelizing preserves semantics:</span><br><span>NaNs are UB &amp; </span><code>-0 == +0</code><span>.</span><br><span>- Consistency in standard.</span></td>
<td><span>- Safer semantics by default.</span><br><span>- Hardware acceleration by default.</span><br><br></td>
</tr>
<tr>
<td><strong><span>Cons</span></strong></td>
<td><span>- The convenient name, </span><code>min</code><span>, has unportable behavior.</span><br><span>- The convenient name has degraded performance.</span><br></td>
<td><span class="ui-comment-inline-span">- Inconsistency in identically named functions.</span><br><span class="ui-comment-inline-span">- Subtle surprises when transitioning to atomics. </span><br></td>
</tr>
<tr>
<td><strong><span>Training reqs.</span></strong></td>
<td><span>Misuse and Performance.</span></td>
<td><span>Subtle surprising behavior change for floating-point.</span></td>
</tr>
<tr>
<td><strong><span>Default</span></strong></td>
<td><span>Stability.</span></td>
<td><span>Correctness + Performance.</span></td>
</tr>
<tr>
<td><strong><span>Opt-in</span></strong></td>
<td><span>Correctness + Performance.</span></td>
<td><span>Stability.</span></td>
</tr>
</tbody>
</table><p><span class="ui-comment-inline-span">C++ needs to make an engineering trade-off: prioritizing consistency in the standard or prioritizing better defaults</span><span>.</span></p><h2 id="Alternatives-that-reserve-fetch_minfetch_max-for-stdminstdmax" data-id="Alternatives-that-reserve-fetch_minfetch_max-for-stdminstdmax"><a class="anchor hidden-xs" href="#Alternatives-that-reserve-fetch_minfetch_max-for-stdminstdmax" title="Alternatives-that-reserve-fetch_minfetch_max-for-stdminstdmax"><span class="octicon octicon-link"></span></a><span>Alternatives that reserve </span><code>fetch_min</code><span>/</span><code>fetch_max</code><span> for </span><code>std::min</code><span>/</span><code>std::max</code></h2><p><span>In this group of alternatives, exposure of different semantics shall happen through other APIs, and we may potentially expose one or more of the following:</span></p><ul>
<li><code>fetch_min</code><span>/</span><code>fetch_max</code><span> with </span><code>std::min</code><span>/</span><code>std::max</code><span> semantics, or</span></li>
<li><code>fetch_fminimum</code><span>/</span><code>fetch_fmaximum</code><span> with </span><code>fminimum</code><span>/</span><code>fmaximum</code><span> semantics, or</span></li>
<li><code>fetch_fminimum_num</code><span>/</span><code>fetch_fmaximum_num</code><span> with </span><code>fminimum_num</code><span>/</span><code>fmaximum_num</code><span> semantics, or</span></li>
<li><code>fetch_min</code><span>/</span><code>fetch_max</code><span> API accepting a defaulted comparison function object </span><code>Cmp</code><span> that satisfies the requirements of </span><a href="https://en.cppreference.com/w/cpp/named_req/Compare" target="_blank" rel="noopener"><span>Compare</span></a><span> and defaults to </span><code>std::less</code><span>, potentially providing other function objects to choose </span><code>minimum</code><span>/</span><code>minimumNumber</code><span> semantics.</span></li>
</ul><pre><code class="cpp hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span></div><div class="code"><span class="token comment">// Listing 4: Same semantics as std::min/std::max.</span>
<span class="token comment">// One or more of the below options</span>

<span class="token keyword">template</span> <span class="token operator">&lt;</span>floating<span class="token operator">-</span>point<span class="token operator">&gt;</span> <span class="token keyword">struct</span> <span class="token class-name">less_fminimum</span><span class="token punctuation">;</span>
<span class="token keyword">template</span> <span class="token operator">&lt;</span>floating<span class="token operator">-</span>point<span class="token operator">&gt;</span> <span class="token keyword">struct</span> <span class="token class-name">less_fminimum_num</span><span class="token punctuation">;</span>
<span class="token keyword">template</span> <span class="token operator">&lt;</span>floating<span class="token operator">-</span>point<span class="token operator">&gt;</span> <span class="token keyword">struct</span> <span class="token class-name">less_fmaximum</span><span class="token punctuation">;</span>
<span class="token keyword">template</span> <span class="token operator">&lt;</span>floating<span class="token operator">-</span>point<span class="token operator">&gt;</span> <span class="token keyword">struct</span> <span class="token class-name">less_fmaximum_num</span><span class="token punctuation">;</span>

<span class="token keyword">template</span> <span class="token operator">&lt;</span>floating<span class="token operator">-</span>point<span class="token operator">&gt;</span>
<span class="token keyword">class</span> <span class="token class-name">atomic</span> <span class="token punctuation">{</span>
    <span class="token keyword">using</span> T <span class="token operator">=</span> floating<span class="token operator">-</span>point<span class="token punctuation">;</span>

    T <span class="token function">fetch_min</span><span class="token punctuation">(</span>T x<span class="token punctuation">,</span> memory_order <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// std::min</span>
    T <span class="token function">fetch_max</span><span class="token punctuation">(</span>T x<span class="token punctuation">,</span> memory_order <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// std::max</span>

    T <span class="token function">fetch_fminimum</span><span class="token punctuation">(</span>T x<span class="token punctuation">,</span> memory_order <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// std::fminimum</span>
    T <span class="token function">fetch_fmaximum</span><span class="token punctuation">(</span>T x<span class="token punctuation">,</span> memory_order <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// std::fmaximum</span>

    T <span class="token function">fetch_fminimum_num</span><span class="token punctuation">(</span>T x<span class="token punctuation">,</span> memory_order <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// std::fminimum_num</span>
    T <span class="token function">fetch_fmaximum_num</span><span class="token punctuation">(</span>T x<span class="token punctuation">,</span> memory_order <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// std::fmaximum_num</span>

    <span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">Compare</span><span class="token operator">&gt;</span>
    T <span class="token function">fetch_max</span><span class="token punctuation">(</span>T other<span class="token punctuation">,</span> Compare cmp <span class="token operator">=</span> std<span class="token double-colon punctuation">::</span>less<span class="token punctuation">,</span> memory_order ord <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">Compare</span><span class="token operator">&gt;</span>
    T <span class="token function">fetch_min</span><span class="token punctuation">(</span>T other<span class="token punctuation">,</span> Compare cmp <span class="token operator">=</span> std<span class="token double-colon punctuation">::</span>less<span class="token punctuation">,</span> memory_order ord <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
    
x<span class="token punctuation">.</span><span class="token function">fetch_min</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span>                  <span class="token comment">// std::min</span>
x<span class="token punctuation">.</span><span class="token function">fetch_min</span><span class="token punctuation">(</span>y<span class="token punctuation">,</span> less_fminimum<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// std::fminimum</span>
</div></div></code></pre><p><span>The advantage of these alternatives is that they preserve the semantics of sequential code that uses </span><code>std::min</code><span> when it is made concurrent via </span><code>fetch_min</code><span>.</span></p><p><span>The disadvantages of these alternatives are that the </span><code>fetch_min</code><span>/</span><code>_max</code><span> semantics are unintuitive for floating-point numbers and error prone, and do not benefit from any hardware acceleration available as surveyed in </span><strong><span>Table 2</span></strong><span>.</span></p><p><span>Generic programmers willing to avoid these disadvantages need to opt-in to wrapping these APIs as follows:</span></p><pre><code class="cpp hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span></div><div class="code"><span class="token comment">// Listing 5: atomic min wrapper</span>
<span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">&gt;</span>
T <span class="token function">atomic_min_wrapper</span><span class="token punctuation">(</span>atomic<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span><span class="token operator">&amp;</span> x<span class="token punctuation">,</span> T<span class="token operator">&amp;</span> y<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token keyword">constexpr</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>is_floating_point_v<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> x<span class="token punctuation">.</span><span class="token function">fetch_fminimum</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> x<span class="token punctuation">.</span><span class="token function">fetch_min</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</div></div></code></pre><p><span>But creating such wrappers requires developers to be aware of essentially everything discussed in this proposal until this point.</span></p><p><span>Such awareness may be enforced by not providing </span><code>fetch_min</code><span>/</span><code>fetch_max</code><span> APIs for floating-point atomics at all, and instead only provide the </span><code>fetch_fminimum</code><span> and related APIs, requiring developers to explicitly pick the semantics, and making wrappers like the one in </span><strong><span>Listing 5</span></strong><span> mandatory for writing generic code.</span></p><h2 id="Alternatives-that-provide-fetch_minfetch_max-with-different-semantics" data-id="Alternatives-that-provide-fetch_minfetch_max-with-different-semantics"><a class="anchor hidden-xs" href="#Alternatives-that-provide-fetch_minfetch_max-with-different-semantics" title="Alternatives-that-provide-fetch_minfetch_max-with-different-semantics"><span class="octicon octicon-link"></span></a><span>Alternatives  that provide </span><code>fetch_min</code><span>/</span><code>fetch_max</code><span> with different semantics</span></h2><p><span>There is precendent in the C++ standard for the atomic operations semantics to deviate from the non-atomic semantics. For example, </span><code>atomic&lt;int&gt;::fetch_add</code><span> wraps around on overflow, instead of exhibiting undefined behavior, see </span><a href="http://eel.is/c++draft/atomics#ref.int-6" target="_blank" rel="noopener"><span>[atomics#ref.int-6]</span></a><span>:</span></p><blockquote>
<p><em><span>Remarks</span></em><span>: For signed integer types, the result is as if the object value and parameters were converted to their corresponding unsigned types, the computation performed on those types, and the result converted back to the signed type.</span></p>
<p><span>[Note 2: There are no undefined results arising from the computation. — end note]</span></p>
</blockquote><p><span>In a similar spirit, </span><code>atomic&lt;floating-point&gt;::fetch_min</code><span>/</span><code>::fetch_max</code><span> operations could be specified to be well-defined for NaNs and to respect </span><code>-0 &lt; +0</code><span>.</span></p><p><span>If we restricts the specification to the semantics available in the scalar floating-point functions, we have 3 different options:</span></p><ul>
<li><code>fminimum</code><span>/</span><code>fmaximum</code><span>: NaNs are propagated as errors and </span><code>-0 &lt; +0</code><span>.</span></li>
<li><span>NaNs are treated as missing data, i.e., the number is returned when one argument is a NaN, and</span>
<ul>
<li><code>fminimum_num</code><span>/</span><code>fmaximum_num</code><span>: </span><code>-0 &lt; +0</code><span>.</span></li>
<li><code>fmin</code><span>/</span><code>fmax</code><span>: </span><code>-0 == +0</code><span> </span><em><span>or</span></em><span> (QoI) </span><code>-0 &lt; +0</code><span>.</span></li>
</ul>
</li>
</ul><p><span>The semantics chosen could be complemented with a </span><code>fetch_min</code><span>/</span><code>fetch_max</code><span> API that accepts a comparison object that needs to satisfy the requirements of </span><a href="https://en.cppreference.com/w/cpp/named_req/Compare" target="_blank" rel="noopener"><span>Compare</span></a><span> but also allows accepting, e.g., one of a set of floating-point-specific blessed comparison-like objects provided by the standard that handle NaNs in specific ways but do not provide a strict weak ordering when NaNs are present. This enables applications to pick whether, e.g., they want to treat NaNs as missing data, or as errors, or whether they want to treat -0 as equivalent to +0, or as +0 greater than -0.</span></p><p><span>For example, C++ could do some or all of:</span></p><pre><code class="cpp hljs"><div class="wrapper"><div class="gutter linenumber"><span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span></div><div class="code"><span class="token comment">// Listing 6: Different semantics as std::min/max</span>

<span class="token keyword">template</span> <span class="token operator">&lt;</span>floating<span class="token operator">-</span>point<span class="token operator">&gt;</span> <span class="token keyword">struct</span> <span class="token class-name">less_fminimum</span><span class="token punctuation">;</span>
<span class="token keyword">template</span> <span class="token operator">&lt;</span>floating<span class="token operator">-</span>point<span class="token operator">&gt;</span> <span class="token keyword">struct</span> <span class="token class-name">less_fminimum_num</span><span class="token punctuation">;</span>
<span class="token keyword">template</span> <span class="token operator">&lt;</span>floating<span class="token operator">-</span>point<span class="token operator">&gt;</span> <span class="token keyword">struct</span> <span class="token class-name">less_fmaximum</span><span class="token punctuation">;</span>
<span class="token keyword">template</span> <span class="token operator">&lt;</span>floating<span class="token operator">-</span>point<span class="token operator">&gt;</span> <span class="token keyword">struct</span> <span class="token class-name">less_fmaximum_num</span><span class="token punctuation">;</span>

<span class="token keyword">template</span> <span class="token operator">&lt;</span>floating<span class="token operator">-</span>point<span class="token operator">&gt;</span>
<span class="token keyword">class</span> <span class="token class-name">atomic</span> <span class="token punctuation">{</span>
    <span class="token keyword">using</span> T <span class="token operator">=</span> floating<span class="token operator">-</span>point<span class="token punctuation">;</span>

    T <span class="token function">fetch_min</span><span class="token punctuation">(</span>T x<span class="token punctuation">,</span> memory_order <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// std::fminimum or std::fminimum_num</span>
    T <span class="token function">fetch_max</span><span class="token punctuation">(</span>T x<span class="token punctuation">,</span> memory_order <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// std::maximum or std::fmaximum_num</span>

    <span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">Compare</span><span class="token operator">&gt;</span>
    T <span class="token function">fetch_max</span><span class="token punctuation">(</span>T other<span class="token punctuation">,</span> Compare cmp <span class="token operator">=</span> fminimum_num<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> memory_order ord <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">Compare</span><span class="token operator">&gt;</span>
    T <span class="token function">fetch_min</span><span class="token punctuation">(</span>T other<span class="token punctuation">,</span> Compare cmp <span class="token operator">=</span> fminimum_num<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> memory_order ord <span class="token operator">=</span> memory_order_seq_cst<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
    
x<span class="token punctuation">.</span><span class="token function">fetch_min</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span>                      <span class="token comment">// std::fminimum_num</span>
x<span class="token punctuation">.</span><span class="token function">fetch_min</span><span class="token punctuation">(</span>y<span class="token punctuation">,</span> less_fminimum<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>     <span class="token comment">// std::fminimum</span>
x<span class="token punctuation">.</span><span class="token function">fetch_min</span><span class="token punctuation">(</span>y<span class="token punctuation">,</span> less_fminimum_num<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// std::fminimum</span>
x<span class="token punctuation">.</span><span class="token function">fetch_min</span><span class="token punctuation">(</span>y<span class="token punctuation">,</span> less<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>              <span class="token comment">// std::less</span>
</div></div></code></pre><p><span>These groups of alternatives give applications the capability to write generic code which may encounter floating-point values and then exhibits reasonable behavior and benefits from hardware acceleration, without requiring developers of being aware of most of what has been discussed in this paper.</span></p><p><span>If a developer encounters different results when porting their sequential code using </span><code>std::min</code><span> to </span><code>fetch_min</code><span>, discovering those differences is required to notice that the developer may want to opt-in to the </span><code>std::min</code><span> semantics. This feedback loop is - for better or worse - more direct than requiring the developer to be a floating-point expert. If the differences are due to NaNs, then the original sequential program already exhibited undefined behavior, but the differences may be due to the treatment of </span><code>-0 == +0</code><span> in the sequential implementation, which may lead to a change of sign in the final result. This is likely to be a defect in the sequential program, but is well-defined in C++. It is still most-likely to be a defect in the concurrent program.</span></p><h1 id="Wording" data-id="Wording"><a class="anchor hidden-xs" href="#Wording" title="Wording"><span class="octicon octicon-link"></span></a><span>Wording</span></h1><p><span>For the illustrative purpose of showing the impact of the changes to unblock </span><a href="https://wg21.link/P0493R4" target="_blank" rel="noopener"><span>P0493R4</span></a><span>, we provide wording that gives </span><code>fetch_min</code><span>/</span><code>fetch_max</code><span> the </span><code>fminimum_num</code><span>/</span><code>fmaximum_num</code><span> semantics. It provides sounder semantics for non-floating-point experts and better performance by default. While this may alter the program outcomes while parallelizing an application, this alteration is likely to be a defect due to an assumption that either NaNs were not present in the input, or that the change </span><code>-0 == +0</code><span> to </span><code>-0 &lt; +0</code><span> would not impact the current program. That being said, it introduces inconsistency with the meaning of “min” and “max” in the standard. The scalar </span><code>fminimum_num</code><span>/</span><code>fmaximum_num</code><span> C23 APIs should become available in C++26, closing the gap with sequential programs.</span></p><p><span>Therefore, we modify [atomics.ref.float] in </span><a href="https://wg21.link/P0493R4" target="_blank" rel="noopener"><span>P0493R4</span></a><span> as follows:</span></p><blockquote>
<p><span>Remarks: For </span><code>fetch_max</code><span> and </span><code>fetch_min</code><span>, the maximum and minimum computation is performed as if by </span><del><code>max</code><span> and </span><code>min</code><span> algorithms [alg.min.max]</span></del><ins><code>std::fmaximum_num</code><span> and </span><code>std::fminimum_num</code><span> [c.math.syn]</span></ins><span>, respectively, with the object value and the first parameter as the arguments.</span></p>
</blockquote><p><span>This requires updating the C standard to C23 (do you see the issue?), for which we’d need to modify </span><a href="https://eel.is/c++draft/intro.scope#2" target="_blank" rel="noopener"><span>[intro.scope#2]</span></a><span>:</span></p><blockquote>
<p><span>C++ is a general purpose programming language based on the C programming language as described in ISO/IEC 9899:</span><del><span>2018</span></del><ins><span>2024</span></ins><span> Programming languages — C (hereinafter referred to as the C standard).</span></p>
</blockquote><p><span>and </span><a href="https://eel.is/c++draft/intro.refs" target="_blank" rel="noopener"><span>[intro.refs]</span></a><span>:</span></p><blockquote>
<p><span>(1.3) ISO/IEC 9899:</span><del><span>2018</span></del><ins><span>2024</span></ins><span>, Programming languages — C</span><br>
<span>[…]</span><br>
<span>(1.10.2) The library described in ISO/IEC 9899:</span><del><span>2018</span></del><ins><span>2024</span></ins><span>, Clause 7, is hereinafter called the C standard library.</span></p>
</blockquote><p><span>so that then we could then add these to </span><a href="https://eel.is/c++draft/cmath.syn" target="_blank" rel="noopener"><span>[cmath.syn]</span></a><span>:</span></p><p><code>namespace std {</code></p><p><ins><span>constexpr floating-point-type fmaximum_num(floating-point-type x, floating-point-type y);</span><br>
<span>constexpr float fmaximum_numf(float x, float y);</span><br>
<span>constexpr long double fmaximum_numl(long double x, long double y);</span></ins></p><p><ins><span>constexpr floating-point-type fminimum_num(floating-point-type x, floating-point-type y);</span><br>
<span>constexpr float fminimum_numf(float x, float y);</span><br>
<span>constexpr long double fminimum_numl(long double x, long double y);</span></ins><br>
<code>}</code></p><p><span>which would only be possible if the C23 standard was already published, which it isn’t.</span></p><p><span>To be able to merge this earlier into the C++26 working draft, then the alternative is to instead modify [atomics.ref.float] in </span><a href="https://wg21.link/P0493R4" target="_blank" rel="noopener"><span>P0493R4</span></a><span> to include the </span><code>fminimum_num</code><span>/</span><code>fmaximum_num</code><span> wording from the C23 draft:</span></p><blockquote>
<p><span>Remarks: For </span><code>fetch_max</code><span> and </span><code>fetch_min</code><span>, the maximum and minimum computation </span><del><span>is performed as if by </span><code>max</code><span> and </span><code>min</code><span> algorithms [alg.min.max], respectively, with the object value and the first parameter as the arguments.</span></del><ins><span>is performed by determining the maximum and minimum value of their numeric arguments. They determine the number if one argument is a number and the other is a NaN. For these functions, +0 is considered greater than −0.</span></ins></p>
</blockquote><p><span>To unblock the merging of </span><a href="https://wg21.link/P0493R4" target="_blank" rel="noopener"><span>P0493R4</span></a><span> into the working draft, the only requirement is picking the default semantics of </span><code>fetch_min</code><span>/</span><code>fetch_max</code><span>. Therefore, providing comparators and overloads for the different semantics, is left to a future paper (or future revision of this paper is that is deemed to be required).</span></p><h1 id="References" data-id="References"><a class="anchor hidden-xs" href="#References" title="References"><span class="octicon octicon-link"></span></a><span>References</span></h1><ol>
<li><a href="https://wg21.link/P0493R4" target="_blank" rel="noopener"><span>P0493R4 Atomic maximum/minimum</span></a><span>.</span></li>
<li><a href="https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf" target="_blank" rel="noopener"><span>The Removal/Demotion of MinNum and MaxNum Operations from IEEE 754™-2018</span></a><span>.</span></li>
<li><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2273.pdf" target="_blank" rel="noopener"><span>Min-max functions</span></a><span> Proposal for TS 18661 update WG14 N2273.</span></li>
<li><a href="https://open-std.org/JTC1/SC22/WG14/www/docs/n3096.pdf" target="_blank" rel="noopener"><span>ISO/IEC 9899:2024 C23 standard</span></a><span>.</span></li>
<li><a href="https://iremi.univ-reunion.fr/IMG/pdf/ieee-754-2008.pdf" target="_blank" rel="noopener"><span>ANSI/IEEE Std 754-2008</span></a><span>.</span></li>
<li><a href="https://ieeexplore.ieee.org/document/8766229" target="_blank" rel="noopener"><span>ANSI/IEEE Std 754-2019</span></a><span>.</span></li>
</ol></div>
    <div class="ui-toc dropup unselectable hidden-print" style="display:none;">
        <div class="pull-right dropdown">
            <a id="tocLabel" class="ui-toc-label btn btn-default" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false" title="Table of content">
                <i class="fa fa-bars"></i>
            </a>
            <ul id="ui-toc" class="ui-toc-dropdown dropdown-menu" aria-labelledby="tocLabel">
                <div class="toc"><ul class="nav">
<li><a href="#Atomic-floating-point-minmax" title="Atomic floating-point min/max">Atomic floating-point min/max</a></li>
<li><a href="#Introduction" title="Introduction">Introduction</a></li>
<li><a href="#Survey-of-programming-language-floating-point-minmax-API-semantics" title="Survey of programming language floating-point min/max API semantics">Survey of programming language floating-point min/max API semantics</a><ul class="nav">
<li><a href="#C-stdminstdmax" title="C++ std::min/std::max">C++ std::min/std::max</a></li>
<li><a href="#C-fminfmax" title="C fmin/fmax">C fmin/fmax</a></li>
<li><a href="#C23-minimummaximumminimumNumbermaximumNumber" title="C23 minimum/maximum/minimumNumber/maximumNumber">C23 minimum/maximum/minimumNumber/maximumNumber</a></li>
</ul>
</li>
<li><a href="#Impact-of-replacing-min-with-fminimum_num" title="Impact of replacing min with fminimum_num">Impact of replacing min with fminimum_num</a></li>
<li><a href="#Survey-of-hardware-atomic-floating-point-minmax-API-semantics" title="Survey of hardware atomic floating-point min/max API semantics">Survey of hardware atomic floating-point min/max API semantics</a><ul class="nav">
<li><a href="#Performance-impact-of-atomicltfloating-pointgtfetch_min_max-semantics" title="Performance impact of atomic<floating-point>::fetch_min/_max semantics">Performance impact of atomic&lt;floating-point&gt;::fetch_min/_max semantics</a></li>
</ul>
</li>
<li><a href="#Design-space" title="Design space">Design space</a><ul class="nav">
<li><a href="#Alternatives-that-reserve-fetch_minfetch_max-for-stdminstdmax" title="Alternatives that reserve fetch_min/fetch_max for std::min/std::max">Alternatives that reserve fetch_min/fetch_max for std::min/std::max</a></li>
<li><a href="#Alternatives-that-provide-fetch_minfetch_max-with-different-semantics" title="Alternatives  that provide fetch_min/fetch_max with different semantics">Alternatives  that provide fetch_min/fetch_max with different semantics</a></li>
</ul>
</li>
<li><a href="#Wording" title="Wording">Wording</a></li>
<li><a href="#References" title="References">References</a></li>
</ul>
</div><div class="toc-menu"><a class="expand-toggle" href="#">Expand all</a><a class="back-to-top" href="#">Back to top</a><a class="go-to-bottom" href="#">Go to bottom</a></div>
            </ul>
        </div>
    </div>
    <div id="ui-toc-affix" class="ui-affix-toc ui-toc-dropdown unselectable hidden-print" data-spy="affix" style="top:17px;display:none;" null null>
        <div class="toc"><ul class="nav">
<li><a href="#Atomic-floating-point-minmax" title="Atomic floating-point min/max">Atomic floating-point min/max</a></li>
<li><a href="#Introduction" title="Introduction">Introduction</a></li>
<li><a href="#Survey-of-programming-language-floating-point-minmax-API-semantics" title="Survey of programming language floating-point min/max API semantics">Survey of programming language floating-point min/max API semantics</a><ul class="nav">
<li><a href="#C-stdminstdmax" title="C++ std::min/std::max">C++ std::min/std::max</a></li>
<li><a href="#C-fminfmax" title="C fmin/fmax">C fmin/fmax</a></li>
<li><a href="#C23-minimummaximumminimumNumbermaximumNumber" title="C23 minimum/maximum/minimumNumber/maximumNumber">C23 minimum/maximum/minimumNumber/maximumNumber</a></li>
</ul>
</li>
<li><a href="#Impact-of-replacing-min-with-fminimum_num" title="Impact of replacing min with fminimum_num">Impact of replacing min with fminimum_num</a></li>
<li><a href="#Survey-of-hardware-atomic-floating-point-minmax-API-semantics" title="Survey of hardware atomic floating-point min/max API semantics">Survey of hardware atomic floating-point min/max API semantics</a><ul class="nav">
<li><a href="#Performance-impact-of-atomicltfloating-pointgtfetch_min_max-semantics" title="Performance impact of atomic<floating-point>::fetch_min/_max semantics">Performance impact of atomic&lt;floating-point&gt;::fetch_min/_max semantics</a></li>
</ul>
</li>
<li><a href="#Design-space" title="Design space">Design space</a><ul class="nav">
<li><a href="#Alternatives-that-reserve-fetch_minfetch_max-for-stdminstdmax" title="Alternatives that reserve fetch_min/fetch_max for std::min/std::max">Alternatives that reserve fetch_min/fetch_max for std::min/std::max</a></li>
<li><a href="#Alternatives-that-provide-fetch_minfetch_max-with-different-semantics" title="Alternatives  that provide fetch_min/fetch_max with different semantics">Alternatives  that provide fetch_min/fetch_max with different semantics</a></li>
</ul>
</li>
<li><a href="#Wording" title="Wording">Wording</a></li>
<li><a href="#References" title="References">References</a></li>
</ul>
</div><div class="toc-menu"><a class="expand-toggle" href="#">Expand all</a><a class="back-to-top" href="#">Back to top</a><a class="go-to-bottom" href="#">Go to bottom</a></div>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" crossorigin="anonymous" defer></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gist-embed/2.6.0/gist-embed.min.js" integrity="sha256-KyF2D6xPIJUW5sUDSs93vWyZm+1RzIpKCexxElmxl8g=" crossorigin="anonymous" defer></script>
    <script>
        var markdown = $(".markdown-body");
        //smooth all hash trigger scrolling
        function smoothHashScroll() {
            var hashElements = $("a[href^='#']").toArray();
            for (var i = 0; i < hashElements.length; i++) {
                var element = hashElements[i];
                var $element = $(element);
                var hash = element.hash;
                if (hash) {
                    $element.on('click', function (e) {
                        // store hash
                        var hash = this.hash;
                        if ($(hash).length <= 0) return;
                        // prevent default anchor click behavior
                        e.preventDefault();
                        // animate
                        $('body, html').stop(true, true).animate({
                            scrollTop: $(hash).offset().top
                        }, 100, "linear", function () {
                            // when done, add hash to url
                            // (default click behaviour)
                            window.location.hash = hash;
                        });
                    });
                }
            }
        }

        smoothHashScroll();
        var toc = $('.ui-toc');
        var tocAffix = $('.ui-affix-toc');
        var tocDropdown = $('.ui-toc-dropdown');
        //toc
        tocDropdown.click(function (e) {
            e.stopPropagation();
        });

        var enoughForAffixToc = true;

        function generateScrollspy() {
            $(document.body).scrollspy({
                target: ''
            });
            $(document.body).scrollspy('refresh');
            if (enoughForAffixToc) {
                toc.hide();
                tocAffix.show();
            } else {
                tocAffix.hide();
                toc.show();
            }
            $(document.body).scroll();
        }

        function windowResize() {
            //toc right
            var paddingRight = parseFloat(markdown.css('padding-right'));
            var right = ($(window).width() - (markdown.offset().left + markdown.outerWidth() - paddingRight));
            toc.css('right', right + 'px');
            //affix toc left
            var newbool;
            var rightMargin = (markdown.parent().outerWidth() - markdown.outerWidth()) / 2;
            //for ipad or wider device
            if (rightMargin >= 133) {
                newbool = true;
                var affixLeftMargin = (tocAffix.outerWidth() - tocAffix.width()) / 2;
                var left = markdown.offset().left + markdown.outerWidth() - affixLeftMargin;
                tocAffix.css('left', left + 'px');
            } else {
                newbool = false;
            }
            if (newbool != enoughForAffixToc) {
                enoughForAffixToc = newbool;
                generateScrollspy();
            }
        }
        $(window).resize(function () {
            windowResize();
        });
        $(document).ready(function () {
            windowResize();
            generateScrollspy();
        });

        //remove hash
        function removeHash() {
            window.location.hash = '';
        }

        var backtotop = $('.back-to-top');
        var gotobottom = $('.go-to-bottom');

        backtotop.click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            if (scrollToTop)
                scrollToTop();
            removeHash();
        });
        gotobottom.click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            if (scrollToBottom)
                scrollToBottom();
            removeHash();
        });

        var toggle = $('.expand-toggle');
        var tocExpand = false;

        checkExpandToggle();
        toggle.click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            tocExpand = !tocExpand;
            checkExpandToggle();
        })

        function checkExpandToggle () {
            var toc = $('.ui-toc-dropdown .toc');
            var toggle = $('.expand-toggle');
            if (!tocExpand) {
                toc.removeClass('expand');
                toggle.text('Expand all');
            } else {
                toc.addClass('expand');
                toggle.text('Collapse all');
            }
        }

        function scrollToTop() {
            $('body, html').stop(true, true).animate({
                scrollTop: 0
            }, 100, "linear");
        }

        function scrollToBottom() {
            $('body, html').stop(true, true).animate({
                scrollTop: $(document.body)[0].scrollHeight
            }, 100, "linear");
        }
    </script>
</body>

</html>
